如何使用户定义的空默认构造函数的行为类似于编译器定义的空构造函数

R-t*_*tur 8 c++ constructor initialization c++11

我已经尝试学习C ++大约一个月了,但这仍然让我感到困惑。例如,以下“简单”代码:

class A
{
    int m;
public:
    A() = default;
    int getM() { return m; }
};

class B
{
    int m;
public:
    // empty body and empty initializer list
    B() {} // isn't this the same as "B() = default;" ?
    int getM() { return m; }

};

int main()
{
    A a1;
    A a2{};

    std::cout << "a1.m=" << a1.getM() << "\n";
    std::cout << "a2.m=" << a2.getM() << "\n";

    B b1;
    B b2{};

    std::cout << "b1.m=" << b1.getM() << "\n";
    std::cout << "b2.m=" << b2.getM() << "\n";

    std::cin.ignore();
}
Run Code Online (Sandbox Code Playgroud)

结果:

a1.m=...garbage
a2.m=0
b1.m=...garbage
b2.m=...garbage
Run Code Online (Sandbox Code Playgroud)

根据CPP参考,编译器定义的默认构造函数确实具有空主体和空初始化列表。因此,当显式定义具有空主体和空初始化列表的默认构造函数时,如何(在A类中)将成员'm'初始化为零。根据cppreference摘录:

If the implicitly-declared default constructor is not defined as deleted,  it is defined (that is, a function body is generated and compiled)
by the compiler if odr-used, and it has exactly the same effect as a user-defined constructor with empty body and empty initializer list. 
Run Code Online (Sandbox Code Playgroud)

据我了解,两个构造函数的行为应完全相同。看起来很简单,但我不明白。

Nic*_*las 3

这是基本想法。和A a2{};都会对两个对象B b2{};执行所谓的“值初始化”。但是,值初始化的行为方式取决于这些类型的定义方式。

B是一个具有用户提供的默认构造函数的对象。“用户提供”是指为默认构造函数提供主体时的术语。因此,值初始化将调用默认构造函数。该默认构造函数不会初始化其成员,因此成员保持未初始化状态。

A是一个没有用户提供的默认构造函数的对象。它也没有任何其他用户提供的构造函数。并且默认构造函数也没有被删除。并且 . 中没有默认的成员初始值设定项A。鉴于所有这些,值初始化将对对象执行零初始化。这意味着它会在该对象存在之前将全零写入该对象的内存。

规则就是这么规定的;两者的行为并不相同,也不是有意为之。您也无法做任何事情来使用户提供的默认构造函数在所有情况下都像默认的默认构造函数一样。您可以使用户提供的构造函数值初始化其成员,但它始终会这样做,即使您使用默认初始化(B b1;例如)。

规则为什么这么说?因为= default应该等同于空的构造函数体。事实上,与众不同正是其作为一种特征而= default 存在的原因。

当您= default使用默认构造函数时,您是在说“像平常一样生成默认构造函数”。这很重要,因为您可以采取一些措施来主动阻止编译器为您生成默认构造函数。如果您指定其他构造函数(不是复制/移动构造函数),编译器将不会自动生成构造函数。因此,通过使用= default语法,您可以告诉编译器您想要生成的默认构造函数。

相比之下,如果您在默认构造函数中创建一个空主体,则您所说的内容完全不同。您明确地说,“如果用户调用我的默认构造函数,我希望我的成员被默认初始化。” 毕竟,当构造函数中有一个空的成员初始值设定项列表时,这就是意味着什么。所以这就是它应该做的。

如果= default一个空主体的行为相同,那么您将无法获得该行为,也就是说无论如何您都希望对成员进行默认初始化。

基本上,Cppreference的说法是完全错误的;它不具有“与具有空主体和空初始值设定项列表的用户定义构造函数完全相同的效果”。也不应该如此。


如果您想进一步了解值初始化的思想,请考虑这一点。

int i{};
Run Code Online (Sandbox Code Playgroud)

这保证会产生 0 值i。因此,这是合理的:

struct S{int i;};
S s{};
Run Code Online (Sandbox Code Playgroud)

应该为 生成 0 值s.i。这是怎么发生的?因为值初始化会将 零初始化s

那么用户如何表达他们不想要这样,或者想要某种特殊形式的初始化呢?您以与其他所有内容相同的方式进行交流:添加一个构造函数。具体来说,是一个默认构造函数,它执行您想要的初始化形式。