对于默认构造函数和析构函数,“= default”与“{}”有什么不同?

时间:2012-11-27 01:25:52

标签: c++ c++11 user-defined-functions default-constructor deleted-functions

我最初发布这个只是关于析构函数的问题,但现在我正在考虑默认的构造函数。这是最初的问题:

  

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

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

但似乎我可以用更少的打字来获得相同的效果   空定义:

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

这两种定义的表现方式有何不同?

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

Widget() = default;

而不是

Widget() {}

如果在原始帖子违反某些SO规则后延长此问题,我们深表歉意。为默认构造函数发布一个几乎相同的问题让我感到不太理想。

3 个答案:

答案 0 :(得分:85)

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

如果您的析构函数是virtual,则差异可以忽略不计,as Howard pointed out。但是,如果您的析构函数是非虚拟,那么这是一个完全不同的故事。构造函数也是如此。

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

这是C ++ 11定义的一个简单的类:

struct Trivial
{
  int foo;
};

如果尝试默认构造一个,编译器将自动生成默认构造函数。复制/移动和破坏也是如此。因为用户没有提供任何这些成员函数,所以C ++ 11规范认为这是一个“普通”类。因此,这样做是合法的,比如记忆他们的内容以初始化它们等等。

此:

struct NotTrivial
{
  int foo;

  NotTrivial() {}
};

顾名思义,这不再是微不足道的。它有一个用户提供的默认构造函数。如果它是空的并不重要;就C ++ 11的规则而言,这不是一个微不足道的类型。

此:

struct Trivial2
{
  int foo;

  Trivial2() = default;
};

顾名思义,这是一个微不足道的类型。为什么?因为你告诉编译器自动生成默认构造函数。因此,构造函数不是“用户提供的”。因此,类型计算为微不足道,因为它没有用户提供的默认构造函数。

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

答案 1 :(得分:39)

它们都是非平凡的。

它们都具有相同的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.

答案 2 :(得分:31)

之间的重要区别
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是a(可能是   cv-qualified)类类型(第9条)使用用户提供的构造函数   (12.1),然后调用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