49

C# および C++ の回答を探しています。(C# では、「デストラクタ」を「ファイナライザ」に置き換えます)

4

8 に答える 8

57

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();
    }
}

これにより、「ファイナライズ済み」が出力されます

于 2008-10-09T19:07:53.407 に答える
55

前文: 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() ;
}

作成順序は次のとおりです。

  1. m_aObject のコンストラクターが呼び出されます。
  2. m_aData のコンストラクターが呼び出されます。
  3. クラスコンストラクターが呼び出されます
  4. Class コンストラクター内で、m_pThing はその new を持ち、次にコンストラクターが呼び出されます。
  5. クラス コンストラクター内で、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()) ;
}

コンストラクター内でこれらのオブジェクトを作成したい/作成する必要がある場合。

このように、コンストラクターがどこでスローしても、何もリークされません。

于 2008-10-09T19:43:40.573 に答える
11

オブジェクトが完全に構築されていないため、構築中のクラスのデストラクタは呼び出されません。

ただし、オブジェクトが基本クラス オブジェクトである限り構築されているため、その基本クラスのデストラクタ (存在する場合) が呼び出されます。

さらに、メンバー変数には、デストラクタも呼び出されます(他の人が指摘したように)。

注意: これは C++ に適用されます

于 2008-10-09T20:06:34.847 に答える
2

C++ では、答えはノーです。オブジェクトのデストラクタは呼び出されません

ただし、オブジェクトのメンバー データのデストラクタは、いずれかの構築中に例外がスローされない限り、呼び出さます

C++ のメンバー データは、宣言された順序で初期化 (つまり、構築) されるため、コンストラクターがスローされると、メンバー初期化リスト (MIL) で明示的に、またはその他の方法で初期化されたすべてのメンバー データが破棄されます。再び逆順で。

于 2008-10-09T19:14:03.800 に答える
1

C++ の場合、これは前の質問で対処されます: 以下のコードは c++ でメモリ リークを引き起こしますか?

C++ では、コンストラクターで例外がスローされたときにデストラクタは呼び出されませんが、(構築された) オブジェクトのメンバーの dtors が呼び出されるため、これが生のポインターではなくスマート ポインター オブジェクトを使用する主な理由です。このような状況でメモリ リークを防ぐ良い方法です。

于 2008-10-09T21:26:47.760 に答える
1

コンストラクターの実行が終了しない場合、オブジェクトは存在しないため、破棄するものはありません。これは C++ です。C# についてはわかりません。

于 2008-10-09T19:08:19.083 に答える
0

C++ -

いいえ。デストラクタは、部分的に構築されたオブジェクトに対して呼び出されません。警告: デストラクタは、完全に構築されたメンバー オブジェクトに対して呼び出されます。(自動オブジェクトとネイティブ型を含む)

ところで-あなたが本当に探しているのは「スタック巻き戻し」と呼ばれています

于 2008-10-09T19:13:48.370 に答える
0

コンストラクターで例外が発生するようなことをしないでください。

例外をスローできるコンストラクターの後に Initialize() を呼び出します。

于 2008-10-09T19:16:20.143 に答える