投稿に触発されてデストラクタが暗黙の移動メソッドの生成を無効にするのはなぜですか?、同じことが当てはまるかどうか疑問に思いましたデフォルトの仮想デストラクタ、例:.
class WidgetBase // Base class of all widgets
{
public:
virtual ~WidgetBase() = default;
// ...
};
クラスはウィジェット階層の基本クラスであることが意図されているため、基本クラスポインターを操作するときのメモリリークや未定義の動作を回避するために、デストラクタを仮想的に定義する必要があります。一方で、コンパイラーが自動的に移動操作を生成するのを防ぎたくありません。
デフォルトの仮想デストラクタは、コンパイラによって生成された移動操作を防止しますか?
はい、デストラクタを宣言すると、moveコンストラクタの暗黙的な宣言が防止されます。
N3337 [class.copy]/9:
クラスXの定義で移動コンストラクターが明示的に宣言されていない場合、その場合に限り、デフォルトとして暗黙的に宣言されます。
- Xには、ユーザーが宣言したコピーコンストラクターがありません。
- Xには、ユーザーが宣言したコピー代入演算子がありません。
- Xには、ユーザーが宣言したムーブ代入演算子がありません。
- Xにはユーザー宣言のデストラクタがありません、および
- 移動コンストラクターは、削除済みとして暗黙的に定義されません。
デストラクタを宣言し、それをdefault
として定義すると、user-declaredとしてカウントされます。
移動コンストラクターを宣言し、自分でdefault
として定義する必要があります。
WidgetBase(WidgetBase&&) = default;
これにより、コピーコンストラクターがdelete
として定義されるため、これもdefault
する必要があることに注意してください。
WidgetBase(const WidgetBase&) = default;
コピー代入演算子とムーブ代入演算子のルールも非常に似ているため、必要に応じてdefault
する必要があります。
解決策ではありませんが、考えられる回避策の1つです。デフォルトの仮想デストラクタしかないクラスからすべてのクラスを継承できます。
GCC9とAppleのClang ++を-std=c++17
で使用して確認しました。どちらも、以下のクラスを継承するクラスの移動コンストラクターを生成します。
class Object {
public:
virtual ~Object() = default;
};
以下のクラスには、実際にmoveコンストラクターがあります。
class Child : public Object {
public:
Child(std::string data) : data(data) {
}
private:
std::string data;
};
もう1つの考えられるが危険な回避策は、仮想デストラクタをまったく宣言しないことです。次のリスクが発生します。
std::vector
やstd::list
のようなコンテナに格納されている場合は、常にstd::shared_ptr
を使用してラップする必要があります。 std::unique_ptr
はリークを引き起こす可能性があります!これは、削除機能の保存に関連する違いに関連しています。