缺省构造函数和析构函数的“= default”与“{}”有什么不同?

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

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

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

但是,似乎我可以通过使用空白定义来减lessinput来获得相同的效果:

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

这两个定义的行为有什么不同?

基于针对这个问题发布的回复,默认构造函数的情况看起来类似。 鉴于析构函数在“ =default ”和“ {} ”之间的含义几乎没有区别,这些选项对于默认构造函数在意义上几乎没有区别? 也就是说,假设我想创build一个types的对象将被创build和销毁,我为什么要说

 Widget() = default; 

代替

 Widget() {} 

我很抱歉,如果扩大这个问题后,其原来的发布是违反了一些SO规则。 为默认的构造函数发布几乎相同的问题让我觉得不那么理想。

3 Solutions collect form web for “缺省构造函数和析构函数的“= default”与“{}”有什么不同?”

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

正如霍华德指出的 ,如果你的析构函数是virtual ,那么差异是微不足道的。 但是,如果你的析构函数是非虚拟的 ,这是一个完全不同的故事。 构造函数也是如此。

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

按照C ++ 11的定义,这是一个微不足道的类:

 struct Trivial { int foo; }; 

如果你试图默认构造一个,编译器会自动生成一个默认构造函数。 复制/移动和破坏也一样。 由于用户没有提供这些成员函数,C ++ 11规范认为这是一个“微不足道的”类。 因此,这样做是合法的,就像记忆它们的内容来初始化它们等等。

这个:

 struct NotTrivial { int foo; NotTrivial() {} }; 

顾名思义,这不再是微不足道的。 它有一个用户提供的默认构造函数。 它是空的没关系; 就C ++ 11的规则而言,这不是一个微不足道的types。

这个:

 struct Trivial2 { int foo; Trivial2() = default; }; 

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

当你添加阻止创build这样的函数的成员函数时, = default语法主要是用来做复制构造函数/赋值等操作。 但是它也触发了编译器的特殊行为,所以它在默认的构造函数/析构函数中也很有用。

他们都是不平凡的。

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

我目前检测到的唯一区别是,如果Widget包含一个基本或成员与一个无法访问或删除析构函数:

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

然后=default解决scheme将编译,但Widget不会是一个可破坏的types。 也就是说,如果你试图破坏一个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. 

之间的重要区别

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

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

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

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

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

为了初始化Ttypes的对象,意味着:

– 如果T是用户提供的构造函数 (12.1)(可能是cv-qualified)的类types(第9章),则调用T的默认构造函数(如果T没有可访问的默认构造函数);

– 如果T是一个没有用户提供的构造函数的(可能是cv限定的)非联合类types,那么该对象是零初始化的,如果T隐式声明的默认构造函数是非平凡的,则调用该构造函数。

– 如果T是一个数组types,则每个元素都进行了值初始化; 否则,该对象是零初始化的。

例如:

 #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

  • 新的关键字=默认在C + + 11
  • 在编译时计算Cstring的长度。 这真的是一个constexpr?
  • 括号内的初始化列表构造函数
  • 1.0是从std :: generate_canonical有效的输出?
  • 枚举与强types的枚举
  • #include <bits / stdc ++。h>如何在C ++中工作?
  • C + + 11:更正std ::数组初始化?
  • 新的关键字“自动”; 什么时候应该用来声明一个variablestypes?
  • 如何编写启用ADL的追踪返回types或noexcept规范?
  • 类似std :: transform的函数返回转换的容器
  • 为什么C ++ 11不支持指定的初始化列表作为C99?