web-dev-qa-db-ja.com

仮想デストラクタは継承されますか?

仮想デストラクタを持つ基本クラスがある場合。仮想デストラクタも宣言する派生クラスがありますか?

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

class derived : base {
public:
    virtual ~derived () {} // 1)
    ~derived () {}  // 2)
};

具体的な質問:

  1. 1)と2)は同じですか? 2)そのベースのために自動的に仮想化されますか、それとも仮想性を「停止」しますか?
  2. 派生デストラクタは、何の関係もない場合は省略できますか?
  3. 派生デストラクタを宣言するためのベストプラクティスは何ですか?仮想、非仮想、または可能な場合は省略しますか?
75
cairol
  1. はい、それらは同じです。仮想を宣言しない派生クラスは、仮想であることを停止しません。実際、メソッドが基本クラスで仮想化されている場合、派生クラスでメソッド(デストラクタを含む)が仮想化されるのを止める方法はありません。 > = C++ 11では、finalを使用して、派生クラスでオーバーライドされないようにすることができますが、仮想になることを防ぐことはできません。
  2. はい、派生クラスのデストラクタは、何の関係もない場合は省略できます。また、仮想であるかどうかは関係ありません。
  3. 可能な場合は省略します。そして、明確にするために、派生クラスの仮想関数には常にvirtualキーワードを再度使用します。関数が仮想であることを理解するために、人々は継承階層をずっと上に行く必要はありません。さらに、独自のコピーまたは移動コンストラクターを宣言せずにクラスがコピー可能または移動可能である場合、(defaultとして定義した場合でも)あらゆる種類のデストラクタを宣言すると、コピーおよびコンストラクターの宣言が強制されますコンパイラがそれらを挿入しないため、必要に応じて演算子を割り当てます。

項目3の小さなポイントとして、コメントで指摘されているように、デストラクタが宣言されていない場合、コンパイラはデフォルトの(まだ仮想的な)コンパイラを生成します。そして、そのデフォルトはインライン関数です。

インライン関数は、プログラムの他の部分の変更にプログラムをさらす可能性が高く、共有ライブラリのバイナリ互換性を扱いにくいものにします。また、結合の増加により、特定の種類の変更に直面して多くの再コンパイルが発生する可能性があります。たとえば、仮想デストラクタの実装が本当に必要であると判断した場合、それを呼び出したすべてのコードを再コンパイルする必要があります。クラス本体で宣言してから.cppファイルで空に定義した場合、再コンパイルせずに変更しても問題ありません。

私の個人的な選択は、可能な限りそれを省略することです。私の意見では、それはコードを乱雑にし、コンパイラは空の実装よりもデフォルトの実装でわずかに効率的な処理を実行できる場合があります。しかし、あなたが下にあるかもしれない制約があり、それはそれを悪い選択にします。

90
Omnifarious

仮想メンバー関数は、この関数のオーバーロードを暗黙的に仮想化します。

したがって、1)の仮想は「オプション」であり、仮想である基本クラスデストラクタはすべての子デストラクタも仮想にします。

1
Klaim
  1. デストラクタは、すべてのメソッドと同様に自動的に仮想化されます。メソッドがC++で仮想であるのを止めることはできません(既に宣言されている場合、つまり、Javaに「最終」に相当するものがない場合)
  2. はい、省略できます。
  3. このクラスを別のクラスにサブクラス化するかどうかに関係なく、このクラスをサブクラス化する場合は、仮想デストラクタを宣言します。また、メソッドが必要でなくても仮想メソッドを宣言し続けることを好みます。継承を削除することにした場合、サブクラスは機能し続けます。しかし、これは単なるスタイルの問題だと思います。
1
falstro

1 /はい2 /はい、コンパイラーによって生成されます3 /仮想宣言するかどうかの選択は、オーバーライドされた仮想メンバーの規則に従う必要があります-私見、両方の方法で適切な引数があり、1つを選択してそれに従うだけです。

可能な場合は省略しますが、宣言するように促す可能性のあるものが1つあります。コンパイラで生成されたものを使用する場合、暗黙的にインラインになります。インラインメンバー(動的ライブラリなど)を避けたいときがあります。

0
AProgrammer