59

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' には仮想関数がありますが、非仮想デストラクタがあります

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

この警告は安全に無視したり黙らせたりできると思いますか?

4

7 に答える 7

66

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

このテーマに関する Herb Sutter による優れた記事については、こちらを参照してください。記事から:

ガイドライン #4: 基本クラスのデストラクタは、パブリックで仮想、または保護された非仮想のいずれかである必要があります。

于 2008-09-24T14:26:09.317 に答える
9

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

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

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

于 2008-09-24T14:21:48.047 に答える
4

これを行うことを主張する場合は、先に進んで-Wno-non-virtual-dtorGCC に渡してください。この警告はデフォルトではオンになっていないようです。そのため、-Wallまたはで有効にしておく必要があります-Weffc++。ただし、ほとんどの場合、これはバグであるため、これは有用な警告だと思います。

于 2008-09-24T14:37:01.490 に答える
2

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

GCCが泣き言を言っているという観察に同意します。代わりに、ITest* を削除したときに単純に警告する必要があります。本当の危険はそこにある。

于 2008-09-24T14:39:17.973 に答える
2

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

私はこのパターン (小さい 'p') をかなり頻繁に使用していることに気づきました。実際、私のインターフェイスには、公開されている dtor よりも保護された dtor がある方が一般的であることがわかりました。ただし、実際にはそれほど一般的なイディオムではないと思います(それほど話されていません)。警告がGCCに追加されたとき、古い「dtor must be virtual」を試して強制するのが適切だったと思います。仮想関数のルールがあります。個人的に私はそのルールを「仮想機能があり、ユーザーがインターフェースを介してインターフェースのインスタンスを削除できるようにする場合、dtor は仮想でなければならない」に更新しました。

于 2008-09-24T14:40:07.787 に答える
0

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

于 2008-09-24T14:19:41.313 に答える
0

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

于 2008-09-24T14:23:17.577 に答える