web-dev-qa-db-ja.com

仮想デストラクタをデフォルトにする必要がありますか?

次のように宣言されている抽象クラスがあります。

class my_type {
public:
    virtual ~my_type() = default;
    virtual void do_something() = 0;
};

defaultキーワードを使用して、このようなデストラクタを宣言することをお勧めしますか?もっと良い方法はありますか?

も、また = 0デフォルトの実装を指定しない最新の(C++ 11)方法、またはより良い方法はありますか?

30
Humam Helfawi

はい、間違いなくそのようなデストラクタに= defaultを使用できます。特に、それを{}に置き換えるだけの場合。 = defaultの方がより明確だと思います。より明確であるため、すぐに目を引き、疑いの余地はありません。

ただし、その際に考慮すべき注意事項がいくつかあります。

ヘッダーファイルにデストラクタ= defaultするとき(editを参照)(またはそのための特別な関数問題)、それは基本的にヘッダーでそれを定義しています。共有ライブラリを設計するとき、依存するバイナリの再構築を必要とせずに将来より簡単に変更できるように、ヘッダーではなくライブラリによってのみデストラクタを明示的に提供することができます。しかし、繰り返しますが、それは、質問がではないときに、単に= defaultにするか{}にするかです。


EDIT:ショーンがコメントで熱心に言及したように、クラス宣言の外で= defaultを使用することもできます。


もう1つの重要な技術的な違いは、標準では、生成できない明示的にデフォルト設定された関数が生成されないということです。次の例を考えてみましょう。

struct A { ~A() = delete; };
struct B : A { ~B() {}; }

これはnotコンパイルします。これは、Bのデストラクタに対して指定されたコード(およびAのデストラクタの呼び出しなどの暗黙的な要件)をコンパイラに生成させるためです。 --Aのデストラクタが削除されるため、できません。ただし、これを考慮してください。

struct A { ~A() = delete; };
struct B : A { ~B() = default; }

実際、これはwouldコンパイルします。コンパイラは~B()を生成できないと判断するため、単に生成しないからです- -そしてそれを削除済みとして宣言します。つまり、B::~B()を実際に使用または呼び出ししようとするとエラーが発生します。

これには、少なくとも次の2つの意味があることに注意してください。

  1. クラス宣言を含むものをコンパイルするときだけエラーを取得したい場合、それは技術的に有効であるため取得できません。
  2. そのようなデストラクタに常に最初に= defaultを使用する場合、スーパークラスのデストラクタが削除されることを心配する必要はありません。これは一種のエキゾチックな使用ですが、その範囲ではより正確で将来性があります。デストラクタを実際に使用する場合にのみエラーが発生します。そうでない場合は、放置されます。

したがって、防御的なプログラミング手法を使用する場合は、{}の使用を検討することをお勧めします。それ以外の場合は、= defaultingを使用することをお勧めします。これは、正しく動作するコードベースを取得し、意図しない結果を回避するために必要な最小限のプログラム命令を厳守するためです。1


= 0に関して:はい、それはまだ正しい方法です。ただし、実際には「デフォルトの実装がない」ことを指定するのではなく、(1)クラスはインスタンス化できないことに注意してください。 (2)派生クラスはその機能をオーバーライドする必要があります(ただし、スーパークラスによって提供されるオプションの実装を使用できます)。つまり、関数を定義し、それを純粋仮想として宣言することができます。以下に例を示します。

struct A { virtual ~A() = 0; }
A::~A() = default;

これにより、A(およびそのデストラクター)に対するこれらの制約が保証されます。


1)これが予期しない方法で役立つ理由の良い例は、一部の人々が常にreturnをかっこ付きで使用し、C++ 14がdecltype(auto)を追加したことです。括弧を使用する場合と使用しない場合の技術的な違いが本質的に生じました。

28
Yam Marcovic