web-dev-qa-db-ja.com

GNUコンパイラの警告「クラスには仮想関数がありますが、非仮想デストラクタがあります」

C++でインターフェース、つまり純粋な仮想関数のみを含むクラスを定義しました。

インターフェイスのユーザーがインターフェイスへのポインターを介してオブジェクトを削除することを明示的に禁止したいので、インターフェイスの保護された非仮想デストラクタを宣言しました:

class ITest{
public:
    virtual void doSomething() = 0;

protected:
    ~ITest(){}
};

void someFunction(ITest * test){
    test->doSomething(); // ok
    // deleting object is not allowed
    // delete test; 
}

GNUコンパイラは次のような警告を表示します:

クラス「ITest」には仮想関数がありますが、非仮想デストラクタがあります

デストラクタが保護されると、デストラクタを仮想化する場合と非仮想化する場合の違いは何ですか?

この警告は安全に無視または無音にできると思いますか?

58
Paolo Tedesco

多かれ少なかれ、コンパイラのバグです。コンパイラのより新しいバージョンでは、この警告はスローされないことに注意してください(少なくとも4.3ではスローされません)。デストラクタを保護し、非仮想化することは完全に合法です。

このテーマに関するハーブサッターの優れた記事については、 こちら を参照してください。記事から:

ガイドライン#4:基本クラスのデストラクターは、パブリックおよび仮想、または保護された非仮想のいずれかでなければなりません。

65
Greg Rogers

この回答に関するコメントのいくつかは、私が以前に出した回答に関連していますが、これは間違っていました。

保護されたデストラクタは、削除ではなく、基本クラスからのみ呼び出すことができることを意味します。つまり、ITest *は直接削除できず、派生クラスのみが削除できます。派生クラスには、仮想デストラクタが必要になる場合があります。コードには何も問題はありません。

ただし、GCCでローカルで警告を無効にすることはできず、vtableが既にあるため、デストラクタを仮想化することを検討してください。プログラムごとに最大4バイト(クラスインスタンスごとではなく)かかります。派生クラスに仮想dtorを指定した可能性があるため、コストがかからないことがあります。

9
Airsource Ltd

これを行うことを主張する場合は、先に進み、-Wno-non-virtual-dtor GCCへ。この警告はデフォルトではオンになっていないようです。したがって、-Wallまたは-Weffc++。ただし、ほとんどの場合これはバグになるため、有用な警告だと思います。

4
bk1e

これはインターフェイスクラスなので、そのインターフェイスを介してそのインターフェイスを実装しているオブジェクトを削除しないでください。その一般的なケースは、ファクトリーによって作成され、ファクトリーに返されるオブジェクトのインターフェースです。 (オブジェクトにファクトリへのポインタを含めると、非常にコストがかかる場合があります)。

私は、GCCが泣き言を言っているという意見に同意します。代わりに、ITest *を削除すると警告が表示されるだけです。そこに本当の危険があります。

2
MSalters

私の個人的な見解では、あなたは正しいことをしていて、コンパイラが壊れているということです。可能であれば、警告を無効にします(ローカルでインターフェイスを定義するファイルで)。

私はこのパターン(小さな「p」)を非常に多く使用していることがわかりました。実際、インターフェイスを公開するよりも、インターフェイスを保護する方が一般的です。しかし、実際にはそれほど一般的なイディオムだとは思いません(それほど話されません)、GCCに警告が追加されたとき、古い「dtorは仮想でなければならない仮想機能のルールがあります。個人的に、私はルールを「仮想機能があり、ユーザーがインターフェースを介してインターフェースのインスタンスを削除できるようにするにはdtorを仮想にする必要があります。

2
Len Holgate

デストラクタが仮想の場合、基本クラスのデストラクタもクリーンアップを実行する前に呼び出されるようにします。そうしないと、そのコードからリークが発生する可能性があります。そのため、プログラムにそのような警告がないことを確認する必要があります(警告がまったくないことが望ましい)。

0
INS

ITest自体を試そうとするdeleteのメソッドの1つにコードがある場合(悪い考えですが、合法です)、派生クラスのデストラクタは呼び出されません。基本クラスポインターを介して派生インスタンスを削除するつもりがない場合でも、デストラクタを仮想化する必要があります。

0
Adam Rosenfield