Powtarzane wywołania destruktora i uchwyty śledzenia w C ++ / CLI
Bawię się C ++ / CLI, korzystając z dokumentacji MSDN iStandard ECMAi Visual C ++ Express 2010. Uderzyło mnie następujące odejście od C ++:
W przypadku klas ref, zarówno finalizator, jak i destruktor muszą być zapisane, aby mogły być wykonywane wielokrotnie i na obiektach, które nie zostały w pełni skonstruowane.
Wymyśliłem mały przykład:
#include <iostream>
ref struct Foo
{
Foo() { std::wcout << L"Foo()\n"; }
~Foo() { std::wcout << L"~Foo()\n"; this->!Foo(); }
!Foo() { std::wcout << L"!Foo()\n"; }
};
int main()
{
Foo ^ r;
{
Foo x;
r = %x;
} // #1
delete r; // #2
}
Na końcu bloku w#1
, zmienna automatycznax
umiera i wywoływany jest destruktor (który z kolei wywołuje finalizator jawnie, jak to zwykle bywa). To wszystko w porządku. Ale potem ponownie usuwam obiekt poprzez odniesienier
! Wynik jest następujący:
Foo()
~Foo()
!Foo()
~Foo()
!Foo()
Pytania:
Czy jest to niezdefiniowane zachowanie, czy jest to całkowicie dopuszczalne, zadzwonićdelete r
online#2
?
Jeśli usuniemy linię#2
, czy to ma znaczenier
nadal jest uchwytem śledzenia obiektu, który (w sensie C ++) już nie istnieje? Czy to „zwisający uchwyt”? Czy liczenie odniesień pociąga za sobą próbę podwójnego usunięcia?
Wiem, że nie marzeczywisty podwójne usuwanie, ponieważ wyjście ma postać:
Foo()
~Foo()
!Foo()
Nie jestem jednak pewien, czy jest to szczęśliwy wypadek, czy też gwarantowane jest dobrze zdefiniowane zachowanie.
W jakich innych okolicznościach możeburzyciel obiektu zarządzanego można wywołać więcej niż raz?
Czy byłoby dobrze wstawićx.~Foo();
bezpośrednio przed lub por = %x;
?
Innymi słowy, czy zarządzane obiekty „żyją wiecznie” i mogą wywoływać zarówno ich destruktory, jak i ich finalizatory?
W odpowiedzi na zapotrzebowanie @ Hansa na nietrywialną klasę, możesz również rozważyć tę wersję (z destruktorem i finalizatorem dostosowanym do wymagań wielokrotnego wywołania):
ref struct Foo
{
Foo()
: p(new int[10])
, a(gcnew cli::array<int>(10))
{
std::wcout << L"Foo()\n";
}
~Foo()
{
delete a;
a = nullptr;
std::wcout << L"~Foo()\n";
this->!Foo();
}
!Foo()
{
delete [] p;
p = nullptr;
std::wcout << L"!Foo()\n";
}
private:
int * p;
cli::array<int> ^ a;
};