Я играю с С++/CLI, используя документацию MSDN и стандарт ECMA и Visual С++ Express 2010. Что поразило меня был следующий отход от С++:
Для классов ref как финализатор, так и деструктор должны быть записаны так, чтобы их можно было выполнить несколько раз и на объектах, которые еще не были полностью построены.
Я придумал небольшой пример:
#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()
Вопросы:
-
Это поведение undefined, или вполне приемлемо, вызвать
delete r
в строке#2
? -
Если мы удалим строку
#2
, имеет ли значение, чтоr
все еще является дескриптором отслеживания для объекта, который (в смысле С++) больше не существует? Это "оборванная ручка"? Считает ли его подсчет ссылок, что будет предпринята попытка двойного удаления?Я знаю, что нет фактического двойного удаления, поскольку выход становится следующим:
Foo() ~Foo() !Foo()
Тем не менее, я не уверен, что это счастливая случайность или гарантированное четкое поведение.
-
При каких других обстоятельствах деструктор управляемого объекта может вызываться более одного раза?
-
Можно ли вставить
x.~Foo();
непосредственно перед или после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;
};