C++默认构造函数
C++ Default constructor
给定以下代码:
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
}
这两者有什么不同—
temp temp1;
和
temp temp2 = temp();
temp temp1;
这在名为temp1
的实例上调用temp
的默认构造函数。
temp temp2 = temp();
这在临时对象上调用temp
的默认构造函数,然后在temp2
上以临时对象为参数调用编译器生成的副本构造函数(当然,这假设编译器不会消除副本;这取决于编译器的优化设置)。
至于为什么会得到不同的初始化值,标准的第8.5节是相关的:
8.5初始化程序[dcl.init]
第5段:
要零初始化,类型为T
的对象意味着:
- 如果
T
是标量类型(3.9),则将对象设置为转换为T
的值0(零) - 如果
T
是非并集类类型,则每个非静态数据成员和每个基类子对象被零初始化 - 如果
T
是并集类型,则对象的第一个命名数据成员被零初始化 - 如果
T
是数组类型,则每个元素被零初始化 - 如果
T
是引用类型,则不执行初始化
默认初始化类型为T
的对象意味着:
- 如果
T
是非POD类类型(子句9),则调用T
的默认构造函数(如果T
没有可访问的默认构造函数,则初始化格式错误) - 如果
T
是数组类型,则默认初始化每个元素 - 否则,对象初始化为零
对T
类型的对象进行值初始化意味着:
- 如果
T
是具有用户声明构造函数(12.1)的类类型(第9条),则调用T
的默认构造函数(如果T
没有可访问的默认构造函数,则初始化格式错误) - 如果
T
是没有用户声明构造函数的非并集类类型,则T的每个非静态数据成员和基类组件都被值初始化 - 如果
T
是数组类型,则每个元素都被值初始化 - 否则,对象初始化为零
第7段:
初始化器是一组空括号的对象,即(),应进行值初始化。第9段:
如果没有为对象指定初始化器,并且对象是(可能是cv限定的)非POD类类型(或其数组),则该对象应默认初始化;如果对象是const限定类型,则底层类类型应具有用户声明的默认构造函数。否则,如果没有为非静态对象指定初始值设定项,则该对象及其子对象(如果有的话)具有不确定的初始值;如果该对象或其任何子对象是const限定类型,则表示程序格式不正确。12特殊成员功能[特殊]
第7段:
当类用于创建其类类型(1.8)的对象时,会隐式定义该类的隐式声明默认构造函数。隐式定义的默认构造函数执行该类的初始化集,该初始化集将由用户为该类编写的默认构造函数使用空的mem初始值设定项列表(12.6.2)和空的函数体来执行。12.6.2初始化基和成员[class.base.init]
第4段:
如果给定的非静态数据成员或基类不是由mem初始化器id命名的(包括由于构造函数没有ctor初始化器而没有mem初始化程序列表的情况),则- 如果实体是(可能是cv限定的)类类型(或其数组)或基类的非静态数据成员,并且实体类是非POD类,则该实体将被默认初始化(8.5)。如果实体是const限定类型的非静态数据成员,则实体类应具有用户声明的默认构造函数
- 否则,实体不会初始化。如果实体是const限定类型或引用类型,或者(可能是cv限定的)POD类类型(或其数组)包含(直接或间接)const限定型的成员,则程序格式错误
既然规则已经制定好了,让我们看看它们是如何应用的:
temp temp1;
temp
是一个非POD类型(因为它有一个std::string
成员),并且由于没有为temp1
指定初始化器,它将被默认初始化(8.5/9)。这调用默认构造函数(8.5/5)。temp
有一个隐式默认构造函数(12/7),它默认初始化std::string
成员,而int
成员根本没有初始化(12.6.2/4)。
temp temp2 = temp();
另一方面,临时temp
对象被值初始化(8.5/7),该值初始化所有数据成员(8.5/5),它调用std::string
成员中的默认构造函数,零初始化int
成员(8.5%5)
当然,如果您不想在5个以上不同的地方引用该标准,只需确保显式初始化所有内容(例如int i = 0;
或使用初始化器列表)。
代码的行为在很大程度上取决于所使用的编译器。更确切地说,它取决于编译器实现的语言规范的版本。
对于C++98编译器,两个声明对所声明对象的最终值具有相同的影响:str
成员应为空,而num
成员应包含不可预测的值。在这两种情况下,实际初始化都是由temp
类的编译器提供的默认构造函数执行的默认初始化。默认构造函数初始化str
,但未初始化num
。
对于C++03编译器,其行为是不同的。temp1
对象没有区别(它的num
仍然是不可预测的)。但CCD_ 40的初始化处理方式不同。在C++03中,()
初始化器触发了一种新的初始化,即所谓的值初始化。值初始化忽略编译器提供的顶级对象的默认构造函数,而是直接处理其子对象(本例中为数据成员)。因此,temp2
对象通过值初始化有效地初始化,这也将num
成员设置为零(除了用空字符串初始化str
之外)。因此,temp2.num
在C++03编译器中最终为零。
如果在您的实验中,您在temp2.num
中观察到一致的零,则意味着您的编译器在这方面遵循C++03规范。
temp temp1;
将创建一个默认初始化的temp
对象。由于您没有为temp
提供默认构造函数,因此temp
的每个成员也将被默认初始化。由于std::string
提供了一个默认的ctor,因此它可以正确初始化,并具有一个定义良好的值。然而,该整数会被默认初始化,这是实现定义的,通常是一个随机值。
temp temp2 = temp();
这将首先创建一个值初始化的temp
对象。这一点很重要,因为对象本身是值初始化的,其成员也是值初始化的。这对字符串来说并不重要,因为默认值和值初始化是一样的,但对整数来说很重要。值初始化的整数具有值0
然后,您只需将这些成员复制到temp2
中即可。
此外,您可能会对这个相关问题感兴趣
编辑:请参阅我对@In silico的回答的评论,了解为什么MSVC不是这样。:/
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 初始化具有非默认构造函数的std::数组项的更好方法
- 具有默认模板类型的默认构造函数的类型推导
- 如何使用非默认构造函数实例化模板化类
- 为什么不调用移动构造函数?(默认情况下只有构造器,没有别的)
- 有没有一种代码密度较低的方法来使用非默认构造函数初始化数组?
- 声明没有默认构造函数的字段
- 基类中的默认析构函数禁用子类中的移动构造函数(如果有成员)
- 没有默认构造函数作为模板参数的自定义比较器
- C++17 没有默认构造函数的地图放置(私有默认构造函数)
- 使用移动调用对等构造函数unique_ptr默认构造函数
- C++复制构造函数和默认构造函数
- 将向量从 N1 缩小到 N2 项,而不触发默认构造函数并仅使用 move 语义
- 构造函数默认公共和私有变量
- 类模板构造函数默认参数
- 构造函数默认参数
- C++模板构造函数默认参数
- 在c++中设置构造函数默认值
- c++构造函数默认参数
- C++构造函数默认值头文件