5

私はこれに出くわしました:

struct Base {
  void* operator new (size_t);
  void operator delete (void*);
  virtual ~Base () {}  // <--- polymorphic
};
struct Derived : Base {};

void Base::operator delete (void *p)
{
  Base *pB = static_cast<Base*>(p);
  if(dynamic_cast<Derived*>(pB) != 0)
  { /* ... NOT reaching here ? ... */ }
  free(p);
}

もしそうなら、

Base *p = new Derived;
delete p;

驚いたことに、Base::delete 内の条件が満たされ ていません。またはキャストからvoid*の情報を失いDerived*ますか?

4

3 に答える 3

15

関数operator deleteは、rawメモリの割り当て解除関数です。これは、実際のオブジェクト(そのメモリに存在していたもの)がすでに破棄されている場合に呼び出されます。つまり、あなたがあなたの中に入るときまでに、operator deleteオブジェクトはすでに一掃されています。ポインタが指すメモリは本質的に「生」であり、オブジェクトは含まれていません。このrawメモリで多態的な機能を使用しようとしても意味がありません。機能しません。

より正式な用語では、言語標準によれば、重要なデストラクタを持つオブジェクトの存続期間は、デストラクタが開始すると終了します。あなたの場合、すべてのデストラクタはすでに作業を完了しています。オブジェクトの存続期間はすでに終了していますがdynamic_cast、「ライブ」オブジェクトが必要です。

PS正式にはdynamic_cast、いくつかの条件が満たされている限り(12.7 / 5を参照)、デストラクタで使用できますが、すべてのデストラクタが終了すると(あなたの場合のように)、dynamic_cast使用できなくなります。

于 2011-06-22T05:59:27.880 に答える
9

オーバーロードがポインタを取得するoperator deleteと、ポイントされたオブジェクトは破棄されます(~Derived()デストラクタはすでに呼び出されています)。

またはオブジェクトではなくなったため、破棄された後は、Baseまたはオブジェクトのように扱うことはできなくなります。DerivedBaseDerived

于 2011-06-22T05:57:59.243 に答える
2

他の 2 つの回答で既に述べたように、デストラクタが実行されるとオブジェクトの型が変化します。デストラクタが完了すると、その型のオブジェクトは存在しなくなり、そのベース サブオブジェクトのみが存在します (それらのデストラクタが完了するまで)。

この回答の理由は、興味深い実験を提案するためです。このコードの出力はどうなるでしょうか? (まあ、3 つの答えはすべて既に述べていますが、この実験自体は興味深いものです):

#include <iostream>
struct base {
    static void print_type( base const & b ) {   // [1]
        std::cout << b.type() << std::endl;
    }
    virtual std::string type() const {           // [2]
        return "base";
    }
    virtual ~base() { print_type( *this ); }
    base() {          print_type( *this ); }
};
struct derived : base {
    std::string type() const {
        return "derived";
    }
    ~derived() {      print_type( *this ); }
    derived()  {      print_type( *this ); }
};
struct most_derived : derived {
    std::string type() const {
        return "most_derived";
    }
    ~most_derived() { print_type( *this ); }
    most_derived()  { print_type( *this ); }
};
int main() {
    most_derived md;
    base::print_type( md );
}

ノート:

さらに面白いことに、 の呼び出しprint_typeもコンストラクターに追加されます。この関数は、その特定の時点でのオブジェクトの動的タイプの検証として機能します。関数print_type(独立した関数であり、別の翻訳単位で実装されている可能性があります -- コンパイラがその内部をないようにするため)。関数のコンパイル中、コンパイラはそれがコンストラクタ、デストラクタの内部から呼び出されたのか、それらの外部から呼び出されたのかを認識できないため、生成されたコードは動的ディスパッチ メカニズムを使用する必要があり、関数の各ポイントで最終的なオーバーライドにディスパッチされます。時間。

コードの有効性については、§12.7/2 によって保証されています。

クラス X のオブジェクトを参照するポインター (左辺値) を、X の直接または間接基底クラス B へのポインター (参照) に明示的または暗黙的に変換するには、X の構築と、その直接または間接基底のすべての構築B から直接的または間接的に派生するクラスが開始され、これらのクラスの破棄が完了していない必要があります。そうでない場合、変換によって未定義の動作が発生します。オブジェクト obj の直接の非静的メンバーへのポインターを形成する (またはその値にアクセスする) には、obj の構築が開始され、その破棄が完了していない必要があります。それ以外の場合、ポインター値の計算 (またはメンバー値へのアクセス) は完了していません未定義の動作が発生します。

base&への呼び出しでのへの変換は、print_typeオブジェクトの構築が開始された後、各オブジェクトの破棄が完了する前に実行されるため有効です( eachはプログラム内の の各サブオブジェクトを参照します)。most_derived

于 2011-06-22T07:55:17.343 に答える