私は、MSDNドキュメントとECMA標準、およびVisual C ++Express2010を使用してC++/ CLIで遊んでいます。私を驚かせたのは、C++からの次の逸脱でした。
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()
質問:
delete r
オンラインで呼び出すのは未定義の動作ですか、それとも完全に許容でき#2
ますか?行を削除した場合、(C ++の意味で)もはや存在しないオブジェクトの追跡ハンドルである
#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;
};