217

当初はデストラクタのみの質問として投稿しましたが、現在はデフォルト コンストラクタの考慮を追加しています。元の質問は次のとおりです。

クラスに仮想デストラクタを与えたいが、それ以外はコンパイラが生成するものと同じである場合は、次を使用できます=default

class Widget {
public:
   virtual ~Widget() = default;
};

しかし、空の定義を使用して、より少ない入力で同じ効果を得ることができるようです:

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

これら 2 つの定義の動作が異なる方法はありますか?

この質問に対して投稿された回答に基づいて、デフォルトのコンストラクターの状況は似ているようです。=defaultデストラクタの " " と " "の意味にほとんど違いがないことを考えると{}、デフォルト コンストラクタのこれらのオプションにも同様に意味の違いはほとんどないのでしょうか? つまり、そのタイプのオブジェクトが作成され、破棄されるタイプを作成したいと仮定すると、なぜ私は言いたいのですか?

Widget() = default;

それ以外の

Widget() {}

?

元の投稿が SO ルールに違反している後にこの質問を拡張した場合は、お詫び申し上げます。デフォルトのコンストラクターについてほぼ同じ質問を投稿することは、あまり望ましくないオプションだと思いました。

4

3 に答える 3

130

これは、コンストラクタとデストラクタについて尋ねるときのまったく異なる質問です。

ハワードが指摘したように、デストラクタがvirtualの場合、違いはごくわずかです。ただし、デストラクタが非仮想である場合、それはまったく別の話です。コンストラクターについても同じことが言えます。

特別なメンバー関数(デフォルトのコンストラクター、コンストラクター/割り当てのコピー/移動、デストラクタなど)に構文を使用= defaultすることは、単に行うこととは非常に異なることを意味し{}ます。後者の場合、関数は「ユーザー提供」になります。そして、それはすべてを変えます。

これは、C++11の定義による簡単なクラスです。

struct Trivial
{
  int foo;
};

デフォルトのコンストラクターを作成しようとすると、コンパイラーはデフォルトのコンストラクターを自動的に生成します。コピー/移動と破棄についても同じことが言えます。ユーザーがこれらのメンバー関数を提供しなかったため、C++11仕様ではこれを「些細な」クラスと見なしています。したがって、これを行うことは合法です。たとえば、コンテンツを初期化するためにコンテンツをmemcpyするなどです。

これ:

struct NotTrivial
{
  int foo;

  NotTrivial() {}
};

名前が示すように、これはもはや些細なことではありません。これには、ユーザーが提供するデフォルトのコンストラクターがあります。空であるかどうかは関係ありません。C ++ 11のルールに関する限り、これは些細なタイプではありません。

これ:

struct Trivial2
{
  int foo;

  Trivial2() = default;
};

名前が示すように、これも些細なタイプです。なんで?デフォルトのコンストラクターを自動的に生成するようにコンパイラーに指示したためです。したがって、コンストラクターは「ユーザー提供」ではありません。したがって、この型にはユーザー提供のデフォルトコンストラクターがないため、この型は些細なものとしてカウントされます。

構文は主に、その= defaultような関数の作成を妨げるメンバー関数を追加するときに、コピーコンストラクター/割り当てなどを行うためにあります。ただし、コンパイラから特別な動作をトリガーするため、デフォルトのコンストラクタ/デストラクタでも役立ちます。

于 2012-11-27T06:40:46.717 に答える
47

どちらも自明ではありません。

どちらも、ベースとメンバーの noexcept 仕様に応じて、同じ noexcept 仕様を持ちます。

これまでに検出した唯一の違いはWidget、アクセスできない、または削除されたデストラクタを持つベースまたはメンバーが含まれている場合です。

struct A
{
private:
    ~A();
};

class Widget {
    A a_;
public:
#if 1
   virtual ~Widget() = default;
#else
   virtual ~Widget() {}
#endif
};

その後、=defaultソリューションはコンパイルされますWidgetが、破壊可能な型にはなりません。つまり、 a を破棄しようとするとWidget、コンパイル エラーが発生します。しかし、もしそうでなければ、あなたは動くプログラムを持っています。

おお、ユーザー提供のデストラクタを指定すると、を破棄するかどうかに関係なく、コンパイルされませんWidget

test.cpp:8:7: error: field of type 'A' has private destructor
    A a_;
      ^
test.cpp:4:5: note: declared private here
    ~A();
    ^
1 error generated.
于 2012-11-27T02:33:09.763 に答える
46

間の重要な違い

class B {
    public:
    B(){}
    int i;
    int j;
};

class B {
    public:
    B() = default;
    int i;
    int j;
};

で定義されたデフォルトのコンストラクターは、ユーザー定義B() = default;ではないと見なされます。これは、次のような値の初期化の場合、

B* pb = new B();  // use of () triggers value-initialization

コンストラクターをまったく使用しない特別な種類の初期化が行われ、組み込み型の場合、これはzero-initialization になります。この場合は発生B(){}しません。C++ 標準 n3337 § 8.5/7 は言う

タイプ T のオブジェクトを値で初期化するとは、次のことを意味します。

— T がユーザー提供のコンストラクター(12.1)を持つ (おそらく cv 修飾された) クラス型 (第 9 節)である場合 、T のデフォルト コンストラクターが呼び出されます (T にアクセス可能なデフォルト コンストラクターがない場合、初期化の形式は正しくありません)。 );

— T が (おそらく cv 修飾された) 非共用体クラス型 であり、ユーザー提供のコンストラクターを持たない場合、オブジェクトはゼロで初期化され、T の暗黙的に宣言された既定のコンストラクターが自明でない場合、そのコンストラクターが呼び出されます。

— T が配列型の場合、各要素は値で初期化されます。— それ以外の場合、オブジェクトはゼロで初期化されます。

例えば:

#include <iostream>

class A {
    public:
    A(){}
    int i;
    int j;
};

class B {
    public:
    B() = default;
    int i;
    int j;
};

int main()
{
    for( int i = 0; i < 100; ++i) {
        A* pa = new A();
        B* pb = new B();
        std::cout << pa->i << "," << pa->j << std::endl;
        std::cout << pb->i << "," << pb->j << std::endl;
        delete pa;
        delete pb;
    }
  return 0;
}

考えられる結果:

0,0
0,0
145084416,0
0,0
145084432,0
0,0
145084416,0
//...

http://ideone.com/k8mBrd

于 2014-05-16T15:22:09.210 に答える