217

我最初将其发布为仅关于析构函数的问题,但现在我正在添加对默认构造函数的考虑。这是原始问题:

如果我想给我的类一个虚拟的析构函数,但与编译器生成的析构函数相同,我可以使用=default

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

但似乎我可以通过使用空定义减少输入来获得相同的效果:

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

这两个定义有什么不同的表现吗?

根据针对此问题发布的回复,默认构造函数的情况似乎相似。鉴于析构函数的“ =default”和“ {}”之间的含义几乎没有区别,那么默认构造函数的这些选项之间的含义是否也几乎没有区别?也就是说,假设我想创建一个类型,该类型的对象将被创建和销毁,我为什么要说

Widget() = default;

代替

Widget() {}

?

如果在原始发布后扩展此问题违反了某些 SO 规则,我深表歉意。为默认构造函数发布一个几乎相同的问题让我觉得不太理想。

4

3 回答 3

130

当询问构造函数而不是析构函数时,这是一个完全不同的问题。

正如霍华德指出的那样,如果您的析构函数是virtual,那么差异可以忽略不计。但是,如果您的析构函数是non-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,你会得到一个编译时错误。但如果你不这样做,你就有了一个工作程序。

Otoh,如果您提供用户提供的析构函数,那么无论您是否破坏 a ,事情都不会编译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

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

对 T 类型的对象进行值初始化意味着:

— 如果 T 是具有用户提供的构造函数(12.1)的(可能是 cv 限定的)类类型(第 9 条 ),则调用 T 的默认构造函数(如果 T 没有可访问的默认构造函数,则初始化是非良构的);

— 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.

For example:

#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;
}

possible result:

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

http://ideone.com/k8mBrd

于 2014-05-16T15:22:09.210 回答