8

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

2 つのクラスのインターフェイスを作成していますが、タイトルに警告が表示されます。コードは次のとおりです。

class GenericSymbolGenerator {
   protected:                     // <== ok 
    ~GenericSymbolGenerator(void) {}

   public:
    virtual GenericSymbolTableCollection* generateSymbolTableCollection(GenericSymbolTableCollection *gst) = 0;
    GenericSymbolGenerator(void) {}
    // ~GenericSymbolGenerator(void) {} // <== warning if used
};

class PascalPredefinedSymbolGenerator : public GenericSymbolGenerator {
   protected:
    ~PascalPredefinedSymbolGenerator(void) {} // <== ok

   public:
    GenericSymbolTableCollection* generateSymbolTableCollection(GenericSymbolTableCollection *pst); // initializes *pst
    PascalPredefinedSymbolGenerator(void) {}
    // ~PascalPredefinedSymbolGenerator(void) {} <== warning if used
};

class PascalSymbolGenerator : public GenericSymbolGenerator {
    protected:
         ~PascalSymbolGenerator(void) {} // <== ok

    public:
     GenericSymbolTableCollection* generateSymbolTableCollection(GenericSymbolTableCollection *st); // initializes st
     PascalSymbolGenerator(void) {}
     // ~PascalSymbolGenerator(void) {} // <== warning if used
};

コンストラクタ/デストラクタが無効である限り、デストラクタを保護されていると宣言しても問題はありません。クラスがヒープを使用するときに問題が発生します(デストラクタが保護されていると宣言されているため、オブジェクトを「破壊不可能」にする「外部」からクラスを解放する方法はありません)。より便利なアプローチはありますか (完全に公開する以外に)?

4

2 に答える 2

14

ポリモーフィック ベースとして使用するクラスには、仮想デストラクタまたは保護されたデストラクタのいずれかが必要です。

その理由は、公開された非仮想デストラクタがある場合、クラスの外部ユーザーによるほとんどの使用は安全ではないためです。例えば:

GenericSymbolGenerator *ptr = new PascalPredefinedSymbolGenerator();
delete ptr; // behavior is undefined, we tried to call the base class destructor

デストラクタをマークすることにより、ユーザーが基本クラスを介してオブジェクトをprotected削除できないようにします。PascalPredefinedSymbolGeneratorデストラクタを public およびvirtualにすることで、ユーザーが基本クラスを介して削除したときの動作を定義できます。したがって、これらのオプションのいずれかを選択してください。

于 2012-09-30T19:08:41.933 に答える
2

基本クラスの非仮想デストラクタについてコンパイラが警告するのは正しいと私は主張します。継承階層のルートとして明確に意図されたクラスを作成しましたが、基本クラスのデストラクターを非仮想にすることで、基本クラスへのポインターによってオブジェクトを削除する機能を壊してしまいました (すべてが実行されるのは基本クラスのデストラクタであるため、派生クラス固有のアクションは実行されません)。このオブジェクト階層に関しては、ポリモーフィズムの実装を本質的に壊すため、これは C++ では非常に悪い考えと見なされます。

コメントで述べたように、あなたの意図は GenericSymbolGenerator をインターフェイスとして使用し、ユーザーに実際の実装コードを含む派生クラスをインスタンス化して使用させることです。C++ でインターフェイスを宣言する標準的な方法は、インターフェイスで少なくとも 1 つの関数を純粋仮想関数として宣言することです。これにより、基本クラスをインスタンス化することはできなくなりますが、インスタンス化可能な派生クラスは作成できます。generateSymbolTableCollection()これは、基本クラスで純粋仮想関数として宣言することで既に完了しています。したがって、この特定のシナリオでは実際に仮想化する必要があるため、デストラクタを仮想化するだけで済みます。

また、余談ですが、デフォルトのコンストラクターとデストラクタの正規の署名は、通常、パラメーターとして「void」を使用せずに記述されます。代わりに空の括弧を使用してください。

于 2012-09-30T18:55:28.103 に答える