42

基本クラスMediaといくつかの派生クラス、つまりDVDBookなどがあります...基本クラスは次のように記述されます。

class Media{
    private:
        int id;
        string title;
        int year;
    public:
        Media(){ id = year = 0; title = ""; }
        Media(int _id, string _title, int _year): id(_id), title(_title), year(_year) {}
//      virtual ~Media() = 0;
        void changeID(int newID){ id = newID; }
        virtual void print(ostream &out);
};

問題は、デストラクタがないと、GCC から一連の警告が表示されますclass has virtual functions but non-virtual destructorが、それでもコンパイルされ、プログラムは正常に動作するということです。ここで、これらの迷惑な警告を取り除きたいので、仮想デストラクタを追加してコンパイラを満足させます。結果は、次のエラーでコンパイルされません。

undefined reference to `Media::~Media()`

デストラクタを純粋仮想化しても問題は解決しません。それで、何がうまくいかなかったのですか?

4

5 に答える 5

48

追加するだけでなく、仮想デストラクタも定義する必要があります。

//Media.h
class Media{
    //....
    virtual ~Media() = 0;
};

//Media.cpp
#include "Media.h"
//....
Media::~Media() {};

警告が表示される理由は、派生するすべてのクラスに仮想または保護された (クレジット @Steve) デストラクタが必要であるためです。そうしないと、基本クラスへのポインタを介してインスタンスを削除すると、未定義の動作が発生します。

デストラクタが純粋な仮想であっても、デストラクタの定義を提供する必要があることに注意してください。

于 2012-04-05T08:03:34.623 に答える
24

問題は、デストラクタがないと、GCC から「クラスには仮想関数がありますが、非仮想デストラクタがあります」という一連の警告が表示されますが、それでもコンパイルされ、プログラムは正常に動作します。

これは最新の C++ では厄介な警告ですが、古いオブジェクト スタイルの C++ では一般的に正しいです。

問題は、オブジェクトが破壊される方法についてです。簡単なテスト:

#include <iostream>

class Base {};
class Derived: public Base { public: ~Derived() { std::cout << "Aargh\n"; } };

int main() {
  Base* b = new Derived();
  Derived* d = new Derived();

  delete d;
  delete b;
}

これは以下を出力します:

Aargh

うん、一度だけ。

問題はdelete、 type の変数を呼び出すとBase*Base::~Base()メソッドが呼び出されることです。である場合virtual、呼び出しは最終メソッド (この場合は動的型に基づいて) に動的にディスパッチされますDerived::~Derived()が、そうでない場合Derived::~Derived()は呼び出されないため、実行されません。

したがって、基本型を呼び出すdelete(またはそれを行うスマート ポインターを使用する) 場合はvirtual ~Base() {}、それらのクラス定義を追加する必要があります。virtualこれが、デストラクタなしでポリモーフィック クラスを作成するときに gcc が警告する理由です。


注: 時間が変更され、それ以来-Wdelete-non-virtual-dtorClang で実装し、gcc でも複製されました。

-Wnon-virtual-dtorライブラリの作成者には便利ですが (基本クラスで警告されるため)、誤検知率が高くなる可能性があります。一方-Wdelete-non-virtual-dtor、呼び出しサイトで起動し、誤検出率がはるかに低くなります (通常final、クラスの「ポリモーフィック」プロパティを削除するためにペッパーを使用することで回避できます)。

于 2012-04-05T10:02:06.237 に答える
9

あなたがコメントアウトしたのは、デストラクタの純粋仮想宣言です。つまり、そのクラスのオブジェクトをインスタンス化できるようにするには、関数を派生クラスでオーバーライドする必要があります。

必要なのは、デストラクタを仮想関数として定義するだけです。

virtual ~Media() {}

C++ 11 以降では、通常、空の本体を使用する代わりに、これをデフォルトとして定義することをお勧めします。

virtual ~Media() = default;
于 2012-04-05T08:05:58.850 に答える
6

純粋な仮想にするのではなく、仮想デストラクタを実装する必要があります。

詳細については、この同様の質問 (警告ではなく、仮想デストラクタ エラーの観点からはおそらく同じ) を参照してください。

編集:LuchianGrigoreのコメントへの返信として、より一般的な解決策(指摘してくれてありがとう)

上記の質問で指摘されているように、デストラクタを純粋仮想にして実装することもできます。

クラスで仮想デストラクタを使用するのは、基本クラスのインスタンス化を防ぐためです (つまり、クラスを抽象化する純粋仮想メソッドが他にない場合)。

于 2012-04-05T08:04:56.277 に答える
0

最初に宣言をアンコメントしてから、クラス宣言の後に次の行を追加してみてください

Media::~Media(){}
于 2012-04-05T08:08:01.407 に答える