6

以下は、コードの 2 つの断片 (コンパイル可能) です。最初のフラグメントでは、構造体の前方宣言のみを使用しているときに、この構造体へのポインターを基本クラスから削除していますが、Guest クラスの dtor は呼び出されません。
2 番目のフラグメントでは、前方宣言の代わりに、Base での削除を使用してこの Guest クラスの完全な定義を使用し、意図したとおりに動作します。
なんで?なぜ違いが生じるのですか?前方宣言は、このクラス/構造体の定義が別の場所にあるというコンパイラーへの単なるメモではないでしょうか?
それが直感的に機能しないことに非常に驚いています。

//First just forward dclr  
#include "stdafx.h"
#include <iostream>
using std::cout;

struct Guest;

struct Base
{
    Guest* ptr_;
    Base(Guest* ptr):ptr_(ptr)
    {
        cout << "Base\n";
    }
    ~Base()
    {
        cout << "~Base\n";
        delete ptr_;
    }
};

struct Guest
{
    Guest()
    {
        cout << "Guest\n";
        throw std::exception();
    }
    Guest(int)
    {
        cout << "Guest(int)\n";
    }
    ~Guest()
    {
        cout << "~Guest\n";
    }
};

struct MyClass : Base
{
    Guest g;
    MyClass(Guest* g):Base(g)
    {
        cout << "MyClass\n";

    }
    ~MyClass()
    {
        cout << "~MyClass\n";
    }
};
int _tmain(int argc, _TCHAR* argv[])
{
    try
    {
        Guest* g = new Guest(1);
    MyClass mc(g);
    }
    catch(const std::exception& e)
    {
        std::cerr << e.what();
    }
    return 0;
}

// 2 番目 - 完全な定義

#include "stdafx.h"
#include <iostream>
using std::cout;

struct Guest
{
    Guest()
    {
        cout << "Guest\n";
        throw std::exception();
    }
    Guest(int)
    {
        cout << "Guest(int)\n";
    }
    ~Guest()
    {
        cout << "~Guest\n";
    }
};

struct Base
{
    Guest* ptr_;
    Base(Guest* ptr):ptr_(ptr)
    {
        cout << "Base\n";
    }
    ~Base()
    {
        cout << "~Base\n";
        delete ptr_;
    }
};



struct MyClass : Base
{
    Guest g;
    MyClass(Guest* g):Base(g)
    {
        cout << "MyClass\n";

    }
    ~MyClass()
    {
        cout << "~MyClass\n";
    }
};
int _tmain(int argc, _TCHAR* argv[])
{
    try
    {
        Guest* g = new Guest(1);
    MyClass mc(g);
    }
    catch(const std::exception& e)
    {
        std::cerr << e.what();
    }
    return 0;
}
4

6 に答える 6

16

C ++標準(5.3.5 / 5)から:

削除されるオブジェクトの削除時点でのクラスタイプが不完全であり、完全なクラスに重要なデストラクタまたは割り当て解除関数がある場合、動作は定義されていません。

したがって、不完全なタイプに対して削除を使用することはできません。デストラクタを呼び出しますが、コンパイラはまだそれを認識していません。

于 2010-10-26T12:55:02.963 に答える
3

ゲストの定義を知らない限り、ゲストを削除することはできません。デストラクタは呼び出されません。また、Guestがカスタム演算子deleteを定義している場合、それは無視されます。

于 2010-10-26T12:55:01.240 に答える
3

不完全な型へのポインタは削除できません。削除は、タイプを完了する必要がある操作の1つです。HTH

于 2010-10-26T12:55:24.767 に答える
3

非公式:コンパイラは、オブジェクトを正しく削除するためにクラス定義を必要とします。これは、デストラクタの呼び出し方法やoperator deleteそのクラスの呼び出し方法を知っている必要があるためです。

正式には、5.3.5 / 5:

削除されるオブジェクトの削除時点でのクラスタイプが不完全であり、完全なクラスに重要なデストラクタまたは割り当て解除関数がある場合、動作は定義されていません。

たとえばGuest、PODであれば問題ありませんが、デストラクタを指定したため、問題はありません。

于 2010-10-26T12:59:23.847 に答える
2

を呼び出すと、のタイプptr_は不完全ですdelete。これにより、未定義の動作が発生します。したがって、デストラクタは呼び出されない場合があります。Boost.checked_deleteを使用して、このようなシナリオを回避できます。

于 2010-10-26T12:57:08.507 に答える
2

(stdafx.hヘッダーは標準のc ++ではありません。)g ++でコンパイルすると、コンパイラーは以下を生成します。

 warning: possible problem detected in invocation of delete operator:
 warning: invalid use of incomplete type ‘struct Guest’
 warning: forward declaration of ‘struct Guest’
 note: neither the destructor nor the class-specific operator delete will be called, even if they are declared when the class is defined.

適切な警告およびエラーレベルでコンパイルするようにコンパイラーを構成します。

于 2010-10-26T12:57:36.750 に答える