7

次のコアダンプからのバックトレースでは、A2:〜A2が2回呼び出されます。

#0  0x086f5371 in B1::~B1 (this=0xe6d3a030, 
    __in_chrg=<value optimized out>)
    at /fullpath/b1.cpp:400
#1  0x086ffd43 in ~B2 (this=0xe6d3a030, 
    __in_chrg=<value optimized out>)
    at /fullpath/b2.h:21
#2  B2::~B2 (this=0xe6d3a030, 
    __in_chrg=<value optimized out>)
    at /fullpath/b2.h:21
#3  0x086ea516 in A1::~A1 (this=0xe3e93958, 
    __in_chrg=<value optimized out>)
    at /fullpath/a1.cpp:716
#4  0x0889b85d in A2::~A2 (this=0xe3e93958, 
    __in_chrg=<value optimized out>)
    at /fullpath/a2.cpp:216
#5  0x0889b893 in A2::~A2 (this=0xe3e93958, 
    __in_chrg=<value optimized out>)
    at /fullpath/a2.cpp:216
#6  0x0862c0f1 in E::Identify (this=0xe8083e20, t=PT_UNKNOWN)
    at /fullpath/e.cpp:713

A2はA1から派生し、B2はB1から派生します。B2のみにデフォルトのデストラクタがあり、すべての基本クラスのデストラクタは仮想です。

コードは次のようになります。

e.cpp:

E::E(){
    //... some code ...
    myA1= new A2();
}

void E::Identify(){
    //...
    if(myA1){
        delete myA1; //line 713 of e.cpp
        myA1 = NULL;
    }

}

a2.cpp:

A2::~A2(){
    //...
    if (sd) //sd is not null here and also not made null after deletion
    {
        delete [] sd; //when called the second time shouldn't it crash here?
    }
    //...
} // line 216 of a2.cpp

a1.cpp

A1::A1(){
//...
   myB1 = new B2();
//...
}

A1::~A1(){
//...
    delete myB1; //line 716 of a1.cpp
//...
}

同じオブジェクトに対してA2::〜A2が2回呼び出される理由がわかりません(バックトレースのこのポインターは、4フレームと5フレームの両方で同じ値になります)。

フレーム4に移動して逆アセンブルすると、フレーム5の逆アセンブルコードとは非常に異なる結果が出力されます(約90行のアセンブリコードと約20行のアセンブリコード)。

4

2 に答える 2

7

例を最小限に抑えました

#include <cassert>
class A1 {
public:
    virtual ~A1() {
        assert(false);
    }
};

class A2 : public A1 {
};

int main() {
    A1* a = new A2;
    delete a;
    return 0;
}

アサートでコア ダンプをトリガーします。

g++ 4.7.2 でコンパイルすると、gdb で二重のデストラクタ バックトレースが得られます

#0  0x00007f16060e92c5 in raise () from /usr/lib/libc.so.6
#1  0x00007f16060ea748 in abort () from /usr/lib/libc.so.6
#2  0x00007f16060e2312 in __assert_fail_base () from /usr/lib/libc.so.6
#3  0x00007f16060e23c2 in __assert_fail () from /usr/lib/libc.so.6
#4  0x00000000004007c8 in A1::~A1 (this=0xf60010, __in_chrg=<optimized out>) at double.cpp:6
#5  0x000000000040084d in A2::~A2 (this=0xf60010, __in_chrg=<optimized out>) at double.cpp:10
#6  0x0000000000400880 in A2::~A2 (this=0xf60010, __in_chrg=<optimized out>) at double.cpp:10
#7  0x000000000040078c in main () at double.cpp:15

g++ 4.3.2 でコンパイルされた同じコードのバックトレースは似ていますが、A2::~A2 のフレームが 1 つしかありません。

どちらのバックトレースも同じバージョンの gdb (7.5.1) で抽出されました。

したがって、これは g++ 4.7 によって生成されたコードのアーティファクトであり、コンパイルされたバイナリの動作について心配する必要はありません。デストラクタへの実際の二重呼び出しではありません。

于 2013-03-06T09:58:48.263 に答える
2

これはあなたのシナリオかもしれません(ただし、コードのこの部分を見せてくれませんでした)...

クラスEが A2 へのプライベート メンバー ポインターを保持し、コピー コンストラクターまたは operator= を持たない場合 ....

次に、タイプ E のオブジェクトが、デフォルトのコピー コンストラクターまたは演算子 = を使用してタイプ E の別のオブジェクト (変数) にコピーされる状況が発生する可能性があります。

これにより、メンバーの浅いコピーが発生し、両方のオブジェクトが同じオブジェクト A1 を指すようになります。

オブジェクト E が破棄されると、両方とも同じ A2オブジェクトを削除しようとします。

于 2013-02-05T15:52:51.940 に答える