0

C++クイズでこの質問(初心者C++)がありました:私の答えは間違っていました。正解の背後にある説明を理解したい-「未定義の動作」

質問: 関数 foo() が戻った後、次のコードで何が起こりますか?

class base
{
    public:
        base() { }
        ~base() { }
};

class derived : public base
{
    private:
        int *p_pi_values;

    public:
        derived() : p_pi_values(new int[100]) {  }
        ~derived() { delete [] p_pi_values; }
};

void foo(void)
{
    derived *p_derived = new derived();
    base *p_base = p_derived;

    // Do some other stuff here.

    delete p_base;
}

私はこの答えを出しましたが、間違っていることがわかりました ==> 整数配列は適切に削除されません。

正解 ==> 動作は未定義です。

4

4 に答える 4

9

基本クラスのデストラクタはそうではありませんvirtual。基本サブオブジェクトへのポインタを介してオブジェクトを削除する場合、対応する基本クラスには仮想デストラクタが必要であり、そうでない場合は未定義の動作であるというのは、単に言語の規則です。

(実際には、基本クラスに仮想デストラクタがない場合、コンパイラは、派生オブジェクトに必要なすべてのクリーンアップを実行するために必要なコードを出力しない可能性があります。オブジェクトがと同じタイプであると想定するだけです。実際、最も派生したオブジェクトの多態的なルックアップには、不必要に課したくないコストがかかるため、ポインタであり、さらに調べる必要はありません。)

§5.3.5/3:

最初の選択肢(オブジェクトの削除)では、オペランドの静的型が動的型と異なる場合、静的型はオペランドの動的型の基本クラスであり、静的型は仮想デストラクタを持っているか、動作が定義されていません。

于 2012-08-09T22:04:08.383 に答える
2

基本クラスでデストラクタを仮想にする必要があります。現在のコードの問題はdelete p_base、基本クラスのデストラクタが呼び出されることです。派生クラスからのものは呼び出されず、整数の配列に割り当てられたメモリは解放されません。

これは、メソッドが基本クラスで仮想でない場合、コンパイラは単純にポインター型を調べて、この型 (この場合は基本クラス) にあるメソッドを呼び出すためです。つまり、どのメソッドを呼び出すかを決定します。ポインターが参照しているオブジェクトの実際の型ではなく、ポインターの型に基づくコンパイル時間。

于 2012-08-09T22:06:00.367 に答える
0

好奇心から、C++ の仕様を確認します。質問に対する答えは、セクション 5.3.5 の項目 3 です。

最初の選択肢 (オブジェクトの削除) では、削除されるオブジェクトの静的タイプがその動的タイプと異なる場合、静的タイプは削除されるオブジェクトの動的タイプの基本クラスであり、静的タイプは仮想デストラクタまたは動作は未定義です。

個人的には、あなたと同じように答えたでしょう。コンパイラの警告を無視すると、派生クラスのデストラクタが呼び出されない可能性が最も高くなります。

于 2012-08-09T22:52:41.580 に答える
-3

コンパイラーはこのコードを最適化することが許可されているため、 p_built から p_base への割り当ては決して起こらないと思います。

より具体的には、コンパイラはコードを 1 行に最適化する場合があります。

新しい派生()を削除します。

したがって、コンパイラが実際にコードを最適化する方法が変わる可能性があるため、動作は未定義と見なされます。

于 2012-08-09T22:06:25.860 に答える