Повторные вызовы деструктора и отслеживание маркеров в C ++ / CLI
Я играю с C ++ / CLI, используя документацию MSDN иСтандарт ECMAи Visual C ++ Express 2010. Меня поразил следующий отход от C ++:
For ref classes, both the finalizer and destructor must be written so they can be executed multiple times and on objects that have not been fully constructed.
Я придумал маленький пример:
#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
}
В конце блока в#1
, автоматическая переменнаяx
умирает, и вызывается деструктор (который, в свою очередь, вызывает финализатор явно, как это принято в обычной идиоме). Это все хорошо и хорошо. Но потом я снова удаляю объект по ссылкеr
! Вывод такой:
Foo()
~Foo()
!Foo()
~Foo()
!Foo()
Questions:
Is it undefined behavior, or is it entirely acceptable, to call delete r
on line #2
?
If we remove line #2
, does it matter that r
is still a tracking handle for an object that (in the sense of C++) no longer exists? Is it a "dangling handle"? Does its reference counting entail that there will be an attempted double deletion?
I know that there isn't an actual double deletion, as the output becomes this:
Foo()
~Foo()
!Foo()
However, I'm not sure whether that's a happy accident or guaranteed to be well-defined behaviour.
Under which other circumstances can the destructor of a managed object be called more than once?
Would it be OK to insert x.~Foo();
immediately before or after r = %x;
?
Другими словами, управляемые объекты «живут вечно» и могут ли их деструкторы и их финализаторы вызываться снова и снова?
В ответ на требование @ Hans к нетривиальному классу вы также можете рассмотреть эту версию (с деструктором и финализатором, выполненными в соответствии с требованием множественного вызова):
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;
};