20
class someclass {};

class base
{
    int a;
    int *pint;
    someclass objsomeclass;
    someclass* psomeclass;
public:
    base()
    {
        objsomeclass = someclass();
        psomeclass = new someclass();
        pint = new int(); 
        throw "constructor failed";
        a = 43;
    }
}

int main()
{
    base temp();
}

上記のコードでは、コンストラクターがスローします。どのオブジェクトがリークされ、メモリ リークをどのように回避できますか?

int main()
{
    base *temp = new base();
}

上記のコードではどうですか?コンストラクターがスローした後にメモリリークを回避するにはどうすればよいですか?

4

7 に答える 7

40

はい、メモリリークします。コンストラクタがスローするとき、デストラクタは呼び出されません (この場合、動的に割り当てられたオブジェクトを解放するデストラクタは表示されませんが、デストラクタがあると仮定します)。

これがスマート ポインターを使用する主な理由です。スマート ポインターは完全なオブジェクトであるため、例外のスタックの巻き戻し中に呼び出されるデストラクタを取得し、メモリを解放する機会があります。

Boost の scoped_ptr<> テンプレートのようなものを使用すると、クラスは次のようになります。

class base{
    int a;
    scoped_ptr<int> pint;
    someclass objsomeclass;
    scoped_ptr<someclass> psomeclass;
    base() : 
       pint( new int),
       objsomeclass( someclass()),
       psomeclass( new someclass())

    {
        throw "constructor failed";
        a = 43;
    }
}

また、メモリ リークは発生しません (また、デフォルトの dtor は動的メモリ割り当てをクリーンアップします)。


要約すると (そしてうまくいけば、これは

base* temp = new base();

声明):

コンストラクター内で例外がスローされた場合、オブジェクトの構築が中止されたときに発生した可能性のあるリソース割り当てを適切に処理するという点で、注意すべき点がいくつかあります。

  1. 構築中のオブジェクトのデストラクタは呼び出されません。
  2. そのオブジェクトのクラスに含まれるメンバー オブジェクトのデストラクタが呼び出されます
  3. 構築されていたオブジェクトのメモリが解放されます。

これは、オブジェクトがリソースを所有している場合、コンストラクターがスローしたときに既に取得されている可能性のあるリソースをクリーンアップするために使用できる 2 つのメソッドがあることを意味します。

  1. 例外をキャッチし、リソースを解放してから再スローします。これを修正するのは難しく、メンテナンスの問題になる可能性があります。
  2. オブジェクトを使用してリソースの有効期間 (RAII) を管理し、それらのオブジェクトをメンバーとして使用します。オブジェクトのコンストラクターが例外をスローすると、メンバー オブジェクトは呼び出されたデストラクターを持ち、責任のあるライフタイムを持つリソースを解放する機会があります。
于 2008-09-29T05:04:45.167 に答える
5

両方の新しいものがリークされます。

ヒープで作成されたオブジェクトのアドレスを名前付きスマート ポインターに割り当てて、例外がスローされたときに呼び出されるスマート ポインター デストラクタ内で削除されるようにします ( RAII )。

class base {
    int a;
    boost::shared_ptr<int> pint;
    someclass objsomeclass;
    boost::shared_ptr<someclass> psomeclass;

    base() :
        objsomeclass( someclass() ),
        boost::shared_ptr<someclass> psomeclass( new someclass() ),
        boost::shared_ptr<int> pint( new int() )
    {
        throw "constructor failed";
        a = 43;
    }
};

コンストラクターで例外がスローされたときにスタックがアンワインドされると、psomeclassおよびpintデストラクタが呼び出され、これらのデストラクタは割り当てられたメモリの割り当てを解除します。

int main(){
    base *temp = new base();
}

(非配置) new を使用した通常のメモリ割り当てでは、コンストラクタが例外をスローすると、オペレータ new によって割り当てられたメモリは自動的に解放されます。個々のメンバーをわざわざ解放する理由 (Mike B の回答へのコメントに応じて) に関しては、自動解放は、新しく割り当てられたオブジェクトのコンストラクターで例外がスローされた場合にのみ適用され、他の場合には適用されません。また、解放されるメモリはオブジェクト メンバーに割り当てられたメモリであり、コンストラクタ内で割り当てた可能性のあるメモリではありません。つまり、メンバー変数apintobjsomeclass、およびpsomeclassのメモリは解放されますが、 new someclass()およびnew int( )から割り当てられたメモリは解放されません。

于 2008-09-29T05:03:27.340 に答える
1

一番上の答えは間違っていて、それでもメモリリークすると思います。コンストラクターが例外をスローした場合、クラス メンバーのデストラクタは呼び出されません(コンストラクターが初期化を完了しておらず、一部のメンバーがコンストラクター呼び出しに達していない可能性があるため)。それらのデストラクタは、クラスのデストラクタ呼び出し中にのみ呼び出されます。それは理にかなっています。

この単純なプログラムはそれを示しています。

#include <stdio.h>


class A
{
    int x;

public:
    A(int x) : x(x) { printf("A constructor [%d]\n", x); }
    ~A() { printf("A destructor [%d]\n", x); }
};


class B
{
    A a1;
    A a2;

public:
    B()
    :   a1(3),
        a2(5)
    {
        printf("B constructor\n");
        throw "failed";
    }
    ~B() { printf("B destructor\n"); }
};


int main()
{
    B b;

    return 0;
}

次の出力 (g++ 4.5.2 を使用):

A constructor [3]
A constructor [5]
B constructor
terminate called after throwing an instance of 'char const*'
Aborted

コンストラクターが途中で失敗した場合、それに対処するのはあなたの責任です。さらに悪いことに、基本クラスのコンストラクターから例外がスローされる可能性があります。これらのケースに対処する方法は、「関数 try ブロック」を使用することです (ただし、その場合でも、部分的に初期化されたオブジェクトの破棄を慎重にコーディングする必要があります)。

問題に対する正しいアプローチは、次のようになります。

#include <stdio.h>


class A
{
    int x;

public:
    A(int x) : x(x) { printf("A constructor [%d]\n", x); }
    ~A() { printf("A destructor [%d]\n", x); }
};


class B
{
    A * a1;
    A * a2;

public:
    B()
    try  // <--- Notice this change
    :   a1(NULL),
        a2(NULL)
    {
        printf("B constructor\n");
        a1 = new A(3);
        throw "fail";
        a2 = new A(5);
    }
    catch ( ... ) {   // <--- Notice this change
        printf("B Cleanup\n");
        delete a2;  // It's ok if it's NULL.
        delete a1;  // It's ok if it's NULL.
    }

    ~B() { printf("B destructor\n"); }
};


int main()
{
    B b;

    return 0;
}

実行すると、割り当てられたオブジェクトのみが破棄されて解放される、期待される出力が得られます。

B constructor
A constructor [3]
B Cleanup
A destructor [3]
terminate called after throwing an instance of 'char const*'
Aborted

必要に応じて、追加のコピーを使用して、スマート共有ポインターを使用して解決できます。次のようなコンストラクタを作成します。

class C
{
    std::shared_ptr<someclass> a1;
    std::shared_ptr<someclass> a2;

public:
    C()
    {
        std::shared_ptr<someclass> new_a1(new someclass());
        std::shared_ptr<someclass> new_a2(new someclass());

        // You will reach here only if both allocations succeeded. Exception will free them both since they were allocated as automatic variables on the stack.
        a1 = new_a1;
        a2 = new_a2;
    }
}

がんばれ、ツヴィ。

于 2012-01-28T12:32:13.673 に答える
0

はい、そのコードはメモリをリークします。「new」を使用して割り当てられたメモリのブロックは、例外が発生したときに解放されません。これはRAIIの背後にある動機の一部です。

メモリ リークを回避するには、次のようにしてください。

psomeclass = NULL;
pint = NULL;
/* So on for any pointers you allocate */

try {
    objsomeclass = someclass();
    psomeclass = new someclass();
    pint = new int(); 
    throw "constructor failed";
    a = 43;
 }
 catch (...)
 {
     delete psomeclass;
     delete pint;
     throw;
 }

于 2008-09-29T05:01:20.417 に答える
0

コンストラクターをスローする場合は、throw の呼び出しの前にあるすべてのものをクリーンアップする必要があります。継承を使用したり、デストラクタをスローしたりする場合は、そうすべきではありません。振る舞いは奇妙です (私の標準は手元にありませんが、未定義かもしれません?)。

于 2008-09-29T05:02:39.290 に答える
-2

「新しい」ものはすべて削除する必要があります。そうしないと、メモリ リークが発生します。したがって、これらの2行:

psomeclass = new someclass();
pint = new int(); 

次のことを行う必要があるため、メモリリークが発生します。

delete pint;
delete psomeclass;

それらがリークされるのを避けるために、finally ブロックで。

また、この行:

base temp = base();

不要です。あなたはただする必要があります:

base temp;

「= base()」を追加する必要はありません。

于 2008-09-29T05:00:12.993 に答える
-3

psomeclassを削除する必要があります...整数をクリーンアップする必要はありません...

RWendi

于 2008-09-29T05:19:34.070 に答える