我最初只是把这个问题作为一个关于析构函数的问题,但现在我增加了对默认构造函数的考虑。这是最初的问题:

如果我想给我的类一个虚的析构函数 否则与编译器生成的结果相同,我可以使用=default: 类小部件{ 公众: virtual ~Widget() = default; }; 但似乎我可以用an少输入就能得到同样的效果 空的定义: 类小部件{ 公众: virtual ~Widget() {} }; 这两个定义有什么不同之处吗?

根据对这个问题的回复,默认构造函数的情况似乎类似。假设对于析构函数,"=default"和"{}"在意义上几乎没有区别,那么对于默认构造函数,这些选项在意义上是否也几乎没有区别呢?也就是说,假设我想创建一个类型,该类型的对象将被创建和销毁,为什么我想说

Widget() = default;

而不是

Widget() {}

?

如果在最初的帖子之后扩展这个问题违反了一些SO规则,我道歉。对默认构造函数提出几乎相同的问题让我觉得不太理想。


当前回答

对于构造函数和析构函数,这是一个完全不同的问题。

如果你的析构函数是虚的,那么差别可以忽略不计,Howard指出。然而,如果你的析构函数是非虚的,情况就完全不同了。构造函数也是如此。

对于特殊的成员函数(默认构造函数,复制/移动构造函数/赋值,析构函数等)使用= default语法意味着与简单地使用{}有很大不同。对于后者,函数变成了“用户提供的”。这改变了一切。

根据c++ 11的定义,这是一个平凡的类:

struct Trivial
{
  int foo;
};

如果您试图默认构造一个构造函数,编译器将自动生成一个默认构造函数。复制/移动和销毁也是如此。因为用户没有提供任何这些成员函数,c++ 11规范认为这是一个“平凡”类。因此这样做是合法的,比如memcpy它们的内容来初始化它们等等。

这样的:

struct NotTrivial
{
  int foo;

  NotTrivial() {}
};

顾名思义,这不再是小事。它有一个用户提供的默认构造函数。它是否为空并不重要;就c++ 11的规则而言,这不是一个平凡的类型。

这样的:

struct Trivial2
{
  int foo;

  Trivial2() = default;
};

顾名思义,这是一个普通类型。为什么?因为您告诉编译器自动生成默认构造函数。因此,构造函数不是“用户提供的”。因此,该类型并不重要,因为它没有用户提供的默认构造函数。

= default语法主要用于复制构造函数/赋值之类的事情,当您添加成员函数时,会阻止此类函数的创建。但它也会触发编译器的特殊行为,所以它在默认构造函数/析构函数中也很有用。

其他回答

它们都是非平凡的。

它们都具有相同的noexcept规范,这取决于基和成员的noexcept规范。

到目前为止,我检测到的唯一区别是,如果Widget包含一个具有不可访问或已删除析构函数的基或成员:

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

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

然后=default解决方案将被编译,但Widget不会是可销毁类型。也就是说,如果你试图销毁一个Widget,你会得到一个编译时错误。但如果你不这样做,你就有了一个有效的程序。

Otoh,如果你提供了用户提供的析构函数,那么不管你是否析构一个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.

对于构造函数和析构函数,这是一个完全不同的问题。

如果你的析构函数是虚的,那么差别可以忽略不计,Howard指出。然而,如果你的析构函数是非虚的,情况就完全不同了。构造函数也是如此。

对于特殊的成员函数(默认构造函数,复制/移动构造函数/赋值,析构函数等)使用= default语法意味着与简单地使用{}有很大不同。对于后者,函数变成了“用户提供的”。这改变了一切。

根据c++ 11的定义,这是一个平凡的类:

struct Trivial
{
  int foo;
};

如果您试图默认构造一个构造函数,编译器将自动生成一个默认构造函数。复制/移动和销毁也是如此。因为用户没有提供任何这些成员函数,c++ 11规范认为这是一个“平凡”类。因此这样做是合法的,比如memcpy它们的内容来初始化它们等等。

这样的:

struct NotTrivial
{
  int foo;

  NotTrivial() {}
};

顾名思义,这不再是小事。它有一个用户提供的默认构造函数。它是否为空并不重要;就c++ 11的规则而言,这不是一个平凡的类型。

这样的:

struct Trivial2
{
  int foo;

  Trivial2() = default;
};

顾名思义,这是一个普通类型。为什么?因为您告诉编译器自动生成默认构造函数。因此,构造函数不是“用户提供的”。因此,该类型并不重要,因为它没有用户提供的默认构造函数。

= default语法主要用于复制构造函数/赋值之类的事情,当您添加成员函数时,会阻止此类函数的创建。但它也会触发编译器的特殊行为,所以它在默认构造函数/析构函数中也很有用。

两者之间的重要区别

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

and

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

是B() = default定义的默认构造函数;视为非用户定义的。这意味着在值初始化的情况下

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

特殊类型的初始化根本不使用构造函数,对于内置类型,这将导致零初始化。在B(){}的情况下,这将不会发生。c++标准n3337§8.5/7规定

To value-initialize an object of type T means: — if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor); — if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, if T’s implicitly-declared default constructor is non-trivial, that constructor is called. — if T is an array type, then each element is value-initialized; — otherwise, the object is zero-initialized.

例如:

#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