Chamadas de destruidor repetidas e identificadores de rastreamento em C ++ / CLI
Estou brincando com o C ++ / CLI, usando a documentação do MSDN ePadrão ECMAe Visual C ++ Express 2010. O que me impressionou foi a seguinte saída do C ++:
Para as classes ref, tanto o finalizador quanto o destruidor devem ser escritos para que possam ser executados várias vezes e em objetos que não foram totalmente construídos.
Eu criei um pequeno exemplo:
#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
}
No final do quarteirão em#1
, a variável automáticax
morre, eo destruidor é chamado (que por sua vez chama o finalizador explicitamente, como é o idioma usual). Está tudo bem e bem. Mas então eu apago o objeto novamente através da referênciar
! A saída é esta:
Foo()
~Foo()
!Foo()
~Foo()
!Foo()
Questões:
É um comportamento indefinido, ou é totalmente aceitável, chamardelete r
conectados#2
?
Se nós removermos a linha#2
, isso importa quer
ainda é um identificador de rastreamento para um objeto que (no sentido de C ++) não existe mais? É uma "alça pendurada"? A sua contagem de referência implica que haverá uma tentativa de eliminação dupla?
Eu sei que não háreal dupla eliminação, como a saída se torna isso:
Foo()
~Foo()
!Foo()
No entanto, não tenho certeza se é um acidente feliz ou se é um comportamento bem definido.
Em que outras circunstâncias pode odestruidor de um objeto gerenciado ser chamado mais de uma vez?
Seria correto inserirx.~Foo();
imediatamente antes ou depoisr = %x;
?
Em outras palavras, os objetos gerenciados "vivem para sempre" e podem ter seus destrutores e seus finalizadores chamados repetidas vezes?
Em resposta à demanda de @ Hans por uma classe não-trivial, você também pode considerar esta versão (com o destruidor e o finalizador feitos de acordo com o requisito de múltiplas chamadas):
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;
};