10

C++ FAQ から:

[11.4] クラスのデストラクタをオーバーロードできますか? いいえ。

これは、戻り値の型、引数の型、または引数の数を変更できないことを意味します。私は単語の構文について髪を分割しているかもしれませんが、親のデストラクタをオーバーライドすることは可能ですか?

class Child : public Parent {
public:
    virtual Parent::~Parent() {
        // New definition
    }
};

さらに言えば、再帰的に行うのですか?

class Grandchild : public Child {
public:
    Child::Parent::~Parent() {
        // An even newer definition
    }
};

これ関連する投稿を読んだことがありますが、デストラクタは継承されず、オーバーライドできないためだと思いますが、明示的に述べられているのを見たことがありません。

編集:親のデストラクタをオーバーライドしたいという事実を反映するようにこれを変更しました。子と孫が ~Parent() をオーバーライドしていることに注意してください。

私がこれを行っている主な理由は、破棄される方法を変更しながら親のインターフェイスを維持することです (子クラスの理由全体)。作成されたすべての親を管理する何か他のものを用意し、後で選択したときにそれらのデストラクタを明示的に呼び出します。

4

4 に答える 4

9

私は言葉の構文について髪を分けているかもしれません

いいえ、そうではありません。これらは 2 つの非常に異なるものです。

しかし、デストラクタをオーバーライドすることは可能ですか?

はい、実際、多くの場合、これを行う必要があります。ただし、これがポリモーフィック オブジェクトに対して機能するためには、基本クラスのデストラクタを として宣言する必要がありvirtualます。

Parent const& p = Child();

仮想p.~Child()であるため、スコープの最後で適切に呼び出されます。Parent::~Parent

于 2013-06-13T17:40:42.047 に答える
8

はい、クラスのデストラクタをオーバーライドすることは可能です。実際、ポリモーフィズムが使用されるクラス階層を定義するときは、基底クラスで仮想デストラクタを宣言する必要があります。

デストラクタのオーバーライドは、通常のメンバー関数のオーバーライドとまったく同じように機能します。つまりdelete、基本クラスへのポインタを介してオブジェクトを破棄することでオブジェクトを破棄すると、派生クラスのデストラクタが適切に呼び出されます。これが、ポリモーフィック階層の基本クラスに仮想デストラクタが必要な理由です。

ただし、仮想デストラクタと仮想メンバー メソッドには違いがありvirtual、デストラクタの性質とは関係ありません。つまり、次のようなコードを実行する場合:

class A
{
public:  
  virtual void Foo() {}
  virtual ~A() {};
};

class B : public A
{
public:
  void Foo() {};
  ~B() {}
};

int main()
{
  A* a = new B;
  a->Foo();  // B::Foo() is called
  delete a;  // B is destroyed via B::~B()
}

... を呼び出すとa->Foo()、 のメソッドFoo()B呼び出されます。B::Foo()は明示的に呼び出さないためA::Foo()A::Foo()呼び出されません。

ただし、オブジェクトが を介して破棄される場合delete a;、最初にデストラクタB::~B()が呼び出され、その後、制御がプログラムに戻る前に基底クラスのデストラクタA::~A()も呼び出されます

もちろんこれは考えてみれば当然のことであり、これもvirtualまたデストラクタの性質とは関係ありませんが、通常のメソッド呼び出しとは動作が異なるvirtualので指摘しておきたいと思いました。

必須の標準見積もり:

[C++03] 12.4/6 : デストラクタ

デストラクタの本体を実行し、本体内に割り当てられた自動オブジェクトを破棄した後、クラス X のデストラクタは、X の直接メンバーのデストラクタ、X の直接基底クラスのデストラクタを呼び出し、X が最も派生したクラスの型である場合 ( 12.6.2)、そのデストラクタは X の仮想基底クラスのデストラクタを呼び出します。すべてのデストラクタは、修飾名で参照されているかのように呼び出されます。つまり、より派生したクラスで可能な仮想オーバーライド デストラクタは無視されます。ベースとメンバーは、コンストラクターの完了とは逆の順序で破棄されます (12.6.2 を参照)。デストラクタ内の return ステートメント (6.6.3) は、呼び出し元に直接戻らない場合があります。呼び出し元に制御を渡す前に、メンバーとベースのデストラクタが呼び出されます。

于 2013-06-13T17:54:02.437 に答える
3

はい: デストラクタを使用できますvirtual。唯一の理由は、派生クラスでそれらをオーバーライドすることです。

次のようになります。

class Parent {
public:
    virtual ~Parent();
};

class Child : public Parent {
public:
    virtual ~Child();
};

class Grandchild : public Child {
public:
    ~Grandchild(); // virtual is inherited here
};

デストラクタは、通常の関数のように名前でオーバーライドされないことに注意してください。これは、名前が常に、インスタンスを破棄するクラスの名前であるためです。

親クラスのデストラクタも常に呼び出されるため、クリーンアップ コードを複製する必要がないことにも注意してください。詳細については、メンバー オブジェクトと基本クラスのサブオブジェクトの構築と破棄の順序を参照してください。


用語

  • 関数のオーバーライドvirtualとは、派生クラスで基底クラスの関数を実装することを意味します。署名をまったく変更することはできません (共変の戻り値の型を使用する場合を除く)。そのため、オーバーライドは常に、継承された仮想関数と同じシグネチャを持ちます。
  • 関数のオーバーロードとは、複数の関数を同じ名前 (およびある意味で同じスコープ) で実装することを意味します。そのため、オーバーロードは、同じ名前を持つ他のものとは常に異なる署名を持ち、仮想ディスパッチに直接関係せず、必ずしも継承されるわけではありません。
于 2013-06-13T17:40:27.360 に答える