C# および C++ の回答を探しています。(C# では、「デストラクタ」を「ファイナライザ」に置き換えます)
8 に答える
C# (以下のコードを参照) では機能しますが、C++ では機能しません。
using System;
class Test
{
Test()
{
throw new Exception();
}
~Test()
{
Console.WriteLine("Finalized");
}
static void Main()
{
try
{
new Test();
}
catch {}
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
これにより、「ファイナライズ済み」が出力されます
前文: Herb Sutter は、この件に関して素晴らしい記事を書いています。
http://herbsutter.wordpress.com/2008/07/25/constructor-exceptions-in-cc-and-java/
C++ : はい、いいえ
コンストラクターがスローした場合 (オブジェクトが「存在しなかった」場合)、オブジェクト デストラクタは呼び出されませんが、内部オブジェクトのデストラクタは呼び出すことができます。
要約すると、オブジェクトのすべての内部部分 (つまり、メンバー オブジェクト) には、構築の逆の順序で呼び出されるデストラクタがあります。コンストラクター内に構築されたものはすべて、何らかの方法で RAII が使用されない限り、そのデストラクタが呼び出されません。
例えば:
struct Class
{
Class() ;
~Class() ;
Thing * m_pThing ;
Object m_aObject ;
Gizmo * m_pGizmo ;
Data m_aData ;
}
Class::Class()
{
this->m_pThing = new Thing() ;
this->m_pGizmo = new Gizmo() ;
}
作成順序は次のとおりです。
- m_aObject のコンストラクターが呼び出されます。
- m_aData のコンストラクターが呼び出されます。
- クラスコンストラクターが呼び出されます
- Class コンストラクター内で、m_pThing はその new を持ち、次にコンストラクターが呼び出されます。
- クラス コンストラクター内で、m_pGizmo はその新しいコンストラクターを呼び出し、次にコンストラクターを呼び出します。
次のコードを使用しているとしましょう。
Class pClass = new Class() ;
考えられるケース:
構築時に m_aData がスローされると、m_aObject のデストラクタが呼び出されます。次に、「new Class」によって割り当てられたメモリが解放されます。
m_pThing が新しい Thing を (メモリ不足で) スローすると、m_aData と m_aObject のデストラクタが呼び出されます。次に、新しいクラスによって割り当てられたメモリが解放されます。
構築時に m_pThing がスローされた場合、「new Thing」によって割り当てられたメモリは割り当て解除されます。次に m_aData、m_aObject のデストラクタが呼び出されます。次に、新しいクラスによって割り当てられたメモリが解放されます。
m_pGizmo が構築時にスローした場合、「新しい Gizmo」によって割り当てられたメモリは割り当て解除されます。次に m_aData、m_aObject のデストラクタが呼び出されます。次に、新しいクラスによって割り当てられたメモリが解放されます。m_pThing がリークしたことに注意してください
基本例外保証を提供したい場合は、コンストラクター内であってもリークしてはなりません。したがって、これを次のように記述する必要があります (STL または Boost を使用):
struct Class
{
Class() ;
~Class() ;
std::auto_ptr<Thing> m_pThing ;
Object m_aObject ;
std::auto_ptr<Gizmo> m_pGizmo ;
Data m_aData ;
}
Class::Class()
: m_pThing(new Thing())
, m_pGizmo(new Gizmo())
{
}
あるいは:
Class::Class()
{
this->m_pThing.reset(new Thing()) ;
this->m_pGizmo.reset(new Gizmo()) ;
}
コンストラクター内でこれらのオブジェクトを作成したい/作成する必要がある場合。
このように、コンストラクターがどこでスローしても、何もリークされません。
オブジェクトが完全に構築されていないため、構築中のクラスのデストラクタは呼び出されません。
ただし、オブジェクトが基本クラス オブジェクトである限り構築されているため、その基本クラスのデストラクタ (存在する場合) が呼び出されます。
さらに、メンバー変数には、デストラクタも呼び出されます(他の人が指摘したように)。
注意: これは C++ に適用されます
C++ では、答えはノーです。オブジェクトのデストラクタは呼び出されません。
ただし、オブジェクトのメンバー データのデストラクタは、いずれかの構築中に例外がスローされない限り、呼び出されます。
C++ のメンバー データは、宣言された順序で初期化 (つまり、構築) されるため、コンストラクターがスローされると、メンバー初期化リスト (MIL) で明示的に、またはその他の方法で初期化されたすべてのメンバー データが破棄されます。再び逆順で。
C++ の場合、これは前の質問で対処されます: 以下のコードは c++ でメモリ リークを引き起こしますか?
C++ では、コンストラクターで例外がスローされたときにデストラクタは呼び出されませんが、(構築された) オブジェクトのメンバーの dtors が呼び出されるため、これが生のポインターではなくスマート ポインター オブジェクトを使用する主な理由です。このような状況でメモリ リークを防ぐ良い方法です。
コンストラクターの実行が終了しない場合、オブジェクトは存在しないため、破棄するものはありません。これは C++ です。C# についてはわかりません。
C++ -
いいえ。デストラクタは、部分的に構築されたオブジェクトに対して呼び出されません。警告: デストラクタは、完全に構築されたメンバー オブジェクトに対して呼び出されます。(自動オブジェクトとネイティブ型を含む)
ところで-あなたが本当に探しているのは「スタック巻き戻し」と呼ばれています
コンストラクターで例外が発生するようなことをしないでください。
例外をスローできるコンストラクターの後に Initialize() を呼び出します。