Llamadas repetidas del destructor y controladores de seguimiento en C ++ / CLI
Estoy jugando con C ++ / CLI, usando la documentación de MSDN y laNorma ECMAy Visual C ++ Express 2010. Lo que me llamó la atención fue la siguiente desviación de C ++:
Para las clases ref, tanto el finalizador como el destructor deben escribirse para que puedan ejecutarse varias veces y en objetos que no se hayan construido completamente.
Yo inventé un pequeño ejemplo:
#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
}
Al final del bloque en#1
, la variable automaticax
muere, y se llama al destructor (que a su vez llama al finalizador explícitamente, como es el lenguaje habitual). Todo esto está bien y bien. Pero luego borro el objeto de nuevo a través de la referencia.r
! La salida es esta:
Foo()
~Foo()
!Foo()
~Foo()
!Foo()
Preguntas:
¿Es un comportamiento indefinido, o es totalmente aceptable, llamardelete r
en linea#2
?
Si quitamos la linea#2
, importa quer
¿Sigue siendo un controlador de seguimiento para un objeto que (en el sentido de C ++) ya no existe? ¿Es un "mango colgando"? ¿Su conteo de referencias implica que habrá un intento de doble eliminación?
Sé que no hay unreal doble eliminación, como la salida se convierte en esto:
Foo()
~Foo()
!Foo()
Sin embargo, no estoy seguro de si se trata de un accidente feliz o si se garantiza que sea un comportamiento bien definido.
¿Bajo qué otras circunstancias puede elincinerador de basuras de un objeto gestionado ser llamado mas de una vez?
¿Estaría bien insertarx.~Foo();
inmediatamente antes o despuésr = %x;
?
En otras palabras, ¿los objetos administrados "viven para siempre" y pueden tener sus destructores y sus finalizadores llamados una y otra vez?
En respuesta a la demanda de @ Hans de una clase no trivial, también puede considerar esta versión (con el destructor y el finalizador hechos para cumplir con el requisito de llamadas múltiples):
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;
};