缺省构造函数和析构函数的“= default”与“{}”有什么不同?
我最初发布这个只是关于析构函数的问题,但现在我添加了默认构造函数的考虑。 这是原来的问题:
如果我想让我的类是一个虚拟的析构函数,但是与编译器生成的相同,我可以使用
=default
:class Widget { public: virtual ~Widget() = default; };
但是,似乎我可以通过使用空白定义来减lessinput来获得相同的效果:
class Widget { public: virtual ~Widget() {} };
这两个定义的行为有什么不同?
基于针对这个问题发布的回复,默认构造函数的情况看起来类似。 鉴于析构函数在“ =default
”和“ {}
”之间的含义几乎没有区别,这些选项对于默认构造函数在意义上几乎没有区别? 也就是说,假设我想创build一个types的对象将被创build和销毁,我为什么要说
Widget() = default;
代替
Widget() {}
?
我很抱歉,如果扩大这个问题后,其原来的发布是违反了一些SO规则。 为默认的构造函数发布几乎相同的问题让我觉得不那么理想。
当询问构造函数而不是析构函数时,这是完全不同的问题。
正如霍华德指出的 ,如果你的析构函数是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 //...