C++默认构造函数

one*_*tar 20 c++ initialization

给出以下代码:

class temp
{
public:
    string str;
    int num;
};

int main()
{
    temp temp1;
    temp temp2 = temp();

    cout << temp1.str << endl; //Print ""
    cout << temp2.str << endl; //Print ""

    cout << temp1.num << endl; //Print a rand num
    cout << temp2.num << endl; //Print 0
}
Run Code Online (Sandbox Code Playgroud)

这两者有什么不同? -

temp temp1;
Run Code Online (Sandbox Code Playgroud)

temp temp2 = temp();
Run Code Online (Sandbox Code Playgroud)

In *_*ico 23

temp temp1;
Run Code Online (Sandbox Code Playgroud)

这会temp在名为的实例上调用默认构造函数temp1.

temp temp2 = temp();
Run Code Online (Sandbox Code Playgroud)

这会temp在临时对象上调用默认构造函数,然后temp2使用临时对象作为参数调用编译器生成的复制构造函数(当然这假定编译器不会删除副本;它取决于编译器的优化设置) .

至于为什么你得到不同的初始值,标准的第8.5节是相关的:


8.5初始值设定项[dcl.init]

第5段:

零初始化的类型的对象T是指:

  • 如果T是标量类型(3.9),则将对象设置为值0(零)转换为T;
  • 如果T是非联合类类型,则每个非静态数据成员和每个基类子对象都是零初始化的;
  • 如果T是联合类型,则对象的第一个命名数据成员为零初始化;
  • if T是数组类型,每个元素都是零初始化的;
  • 如果T是引用类型,则不执行初始化.

默认初始化的类型的对象T是指:

  • 如果T是非POD类类型(第9节),T则调用默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的);
  • 如果T是数组类型,则每个元素都是默认初始化的;
  • 否则,该对象被零初始化.

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

  • if T是具有用户声明的构造函数(12.1)的类类型(第9节),然后T调用默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的);
  • 如果T是没有用户声明的构造函数的非联合类类型,则T的每个非静态数据成员和基类组件都是值初始化的;
  • 如果T是数组类型,则每个元素都是值初始化的;
  • 否则,该对象被零初始化.

第7段:

初始值为空集括号的对象,即(),应进行值初始化.

第9段:

如果没有为对象指定初始化程序,并且该对象是(可能是cv限定的)非POD类类型(或其数组),则该对象应默认初始化; 如果对象是const限定类型,则底层类类型应具有用户声明的默认构造函数.否则,如果没有为非静态对象指定初始化程序,则该对象及其子对象(如果有)具有不确定的初始值; 如果对象或其任何子对象是const限定类型,则程序格式错误.

12特别会员职能[特别]

第7段:

当用于创建类类型(1.8)的对象时,将隐式定义类的隐式声明的默认构造函数.隐式定义的默认构造函数执行该类的初始化集合,该初始化集合将由用户编写的具有空mem-initializer-list(12.6.2)和空函数体的类的默认构造函数执行.

12.6.2初始化基数和成员[class.base.init]

第4段:

如果给定的非静态数据成员或基类没有由mem-initializer-id命名(包括没有mem-initializer-list的情况,因为构造函数没有ctor-initializer),那么

  • 如果实体是(可能是cv限定的)类类型(或其数组)或基类的非静态数据成员,并且实体类是非POD类,则该实体是默认初始化的(8.5).如果实体是const限定类型的非静态数据成员,则实体类应具有用户声明的默认构造函数.
  • 否则,实体未初始化.如果实体是const限定类型或引用类型,或者是(可能是cv限定的)POD类类型(或其数组)包含(直接或间接)const限定类型的成员,则该程序是错误的形成.

现在已经制定了规则,让我们看看它们是如何应用的:

temp temp1;
Run Code Online (Sandbox Code Playgroud)

temp是非POD类型(因为它有一个std::string成员),并且由于没有指定初始化程序temp1,它将被默认初始化(8.5/9).这会调用默认构造函数(8.5/5).temp有一个隐式默认构造函数(12/7),它默认初始化std::string成员,而int成员根本没有初始化(12.6.2/4).

temp temp2 = temp();
Run Code Online (Sandbox Code Playgroud)

另一方面,临时temp对象是值初始化的(8.5/7),它对所有数据成员(8.5/5)进行值初始化,调用std::string成员中的默认构造函数并对成员进行零初始化int(8.5/5) .

当然,如果您不必在5个以上的不同位置引用标准,只需确保明确初始化所有内容(例如int i = 0;或使用初始化列表).


AnT*_*AnT 5

代码的行为主要取决于您使用的编译器.更准确地说,它取决于编译器实现的语言规范版本.

对于C++ 98编译器,两个声明对声明的对象的最终值具有相同的效果:str成员应该变为空,而num成员应该包含不可预测的值.在这两种情况下,实际初始化都是由编译器提供的类的默认构造函数执行的默认初始化temp.该默认构造函数初始化str,但未num初始化.

对于C++ 03编译器,行为是不同的.temp1对象没有区别(它num仍然是不可预测的).但temp2初始化的处理方式不同.在C++ 03中,()初始化器触发了新的初始化 - 所谓的值初始化.值初始化忽略编译器提供的顶级对象的默认构造函数,而是直接在其子对象(本例中为数据成员)上工作.因此,temp2对象通过值初始化有效地初始化,这也将num成员设置为零(除了str用空字符串初始化).因此,temp2.num在C++ 03编译器中最终为零.

如果在您的实验中您观察到一致的零temp2.num,则意味着您的编译器在这方面遵循C++ 03规范.