2010-12-13 14 views
0

デストラクタを呼び出すこれらの3つのアプローチに重大な/重大な相違があるかどうか不思議です。次のコードを考えてみましょう。 main()に記載されている2つのケースも考慮してください。デストラクタを呼び出す方法の比較

class Sample 
{ 
public: 
    ~Sample() 
    { 
     cout << "destructor called" << endl; 
    } 
    void destroyApproach1() { this->~Sample(); } 
    void destroyApproach2() { delete this; } 
}; 

void destroyApproach3(Sample *_this) 
{ 
    delete _this; 
} 

void TestUsingNew() 
{ 
    Sample *pSample[] = { new Sample(), new Sample(),new Sample()}; 
    pSample[0]->destroyApproach1(); 
    pSample[1]->destroyApproach2(); 
    destroyApproach3(pSample[2]); 
} 
void TestUsingPlacementNew() 
{ 
    void *buf1 = std::malloc(sizeof(Sample)); 
    void *buf2 = std::malloc(sizeof(Sample)); 
    void *buf3 = std::malloc(sizeof(Sample)); 
    Sample *pSample[3] = { new (buf1) Sample(), new (buf2) Sample(), new (buf3) Sample()}; 
    pSample[0]->destroyApproach1(); 
    pSample[1]->destroyApproach2(); 
    destroyApproach3(pSample[2]); 
} 
int main() 
{ 
    //Case 1 : when using new 
    TestUsingNew(); 

    //Case 2 : when using placement new 
    TestUsingPlacementNew(); 
    return 0; 
} 

あなたに答えている。その場合へと返信するとき特定してください:ケース1またはケース2、またはその両方!


また、私はこの方法でTestUsingPlacementNew()を書くしようとしていたが、それは実行時例外(MSVC++ 2008)を投げています。私は理由を理解していない:

void TestUsingPlacementNew() 
{ 
    const int size = sizeof(Sample); 
    char *buffer = (char*)std::malloc(size * 3); 
    Sample *pSample[] = { new (buffer) Sample(), new (&buffer[size]) Sample(),new (&buffer[2*size]) Sample()}; 
    pSample[0]->destroyApproach1(); 
    pSample[1]->destroyApproach2(); 
    destroyApproach3(pSample[2]); 
} 

たぶん、メモリパディングおよび/またはアライメントが理由だろうか?


関連トピック:Destructor not called after destroying object placement-new'ed

答えて

5

はい、これらのアプローチの間に大きな違いがあります:

  • destroyApproach1で、あなただけのオブジェクトのデストラクタを呼び出します。実際に占有していたメモリを解放するわけではありません。

  • destroyApproach2

    destroyApproach3オブジェクトのデストラクタを呼び出し、あなたは(delete式を使用して)オブジェクトが占有するメモリを解放します。最初のTestUsingPlacementNewテストでは、オブジェクトが占有していたメモリがmallocの呼び出しによって最初に割り当てられ、newではなく、両方とも間違っています。あなたは配列のインデックス1でオブジェクトをdeleteしようとしているため

あなたの最後のテストでは、実行時エラーが発生します。その要素へのポインタは、最初にnewへの呼び出しから取得されませんでした。最初の例では、3つのポインタすべてが独立したヒープ割り当てになっているため、「動作する」(実際には「動作は未定義ですが、動作は未定義ですが、依然として正しく機能しているように見える」という意味です)。

+0

2番目の段落は新しい配置を使用する場合の2番目の段落ですか? – Nawaz

+0

@Nawaz:私は、答えようとしている部分を示すために答えを更新しました。 –

+0

更新のおかげで:-) – Nawaz

1

delete thisは正しい方法ではありません現代のコードでデストラクタを呼び出すためにあなたはデストラクタを呼び出す必要はありません一般的には:その魔法は、それらが適切な時に呼び出されることです。すべてのケースで

struct A { 
    ~A() { std::cout << "running destructor\n"; } 
}; 

int main() 
{ 
    A a; 
    return 0; 
} 

deletenewによって割り当てられた解放するメモリのためであります。delete thisは、オブジェクトが割り当てられなかったときに問題を引き起こしますnew

struct B { 
    ~B() { delete this } 
}; 

int main() 
{ 
    B b; 
    return 0; 
} 

実際のオペレーティングシステムを搭載したほぼすべてのプラットフォーム(これは技術的には定義されていない動作であり、標準準拠のプログラムが破損してこのケースあなたの指が交差して、あなたのプラットフォームは、あなたが破損したメモリ管理データ構造と無効なスタックとにぶつかるのを許します。


配置newは、主としてポインタが特殊なアドレスに割り当てられなければならないハードウェアデバイスドライバまたは他の場所を意味します。 In general you won't want to destroy objects allocated with placement new。あなたは、単に直接デストラクタを呼び出したい場合には:

My_object* o = new(0xffff) My_object(); 
o->~My_object(); 

をしかし、覚えて、ビャーネ・ストロヴストルップはデストラクタの明示的な呼び出しが...可能な限り避けるべきである」と述べた時折、彼らは不可欠です..。初心者はデストラクタを明示的に呼び出す前に3回考えなければなりませんし、そうする前に経験豊富な同僚に依頼してください "(C++プログラミング言語、10.4.11)。

関連する問題