简体   繁体   English

C ++默认构造函数

[英]C++ Default constructor

Given the following code: 给出以下代码:

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
}

What is the different between these two?— 这两者有什么不同? -

temp temp1;

and

temp temp2 = temp();
temp temp1;

This calls temp 's default constructor on the instance called temp1 . 这会在名为temp1的实例上调用temp的默认构造函数。

temp temp2 = temp();

This calls temp 's default constructor on a temporary object, then calls the compiler-generated copy-constructor on temp2 with the temporary object as the argument (this of course assumes that the compiler doesn't elide copies; it depends on your compiler's optimization settings). 这会在临时对象上调用temp的默认构造函数,然后在临时对象作为参数的情况下调用temp2上编译器生成的复制构造函数(当然这假设编译器不会删除副本;它取决于编译器的优化设置)。

As for why you get different initialized values, section 8.5 of the standard is relevant: 至于为什么你得到不同的初始值,标准的第8.5节是相关的:


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

Paragraph 5: 第5段:

To zero-initialize an object of type T means: 零初始化 T类型的对象意味着:

  • if T is a scalar type (3.9), the object is set to the value of 0 (zero) converted to T ; 如果T是标量类型(3.9),则将对象设置为0(零)转换为T ;
  • if T is a non-union class type, each nonstatic data member and each base-class subobject is zero-initialized; 如果T是非联合类类型,则每个非静态数据成员和每个基类子对象都是零初始化的;
  • if T is a union type, the object's first named data member is zero-initialized; 如果T是联合类型,则对象的第一个命名数据成员为零初始化;
  • if T is an array type, each element is zero-initialized; 如果T是数组类型,则每个元素都是零初始化的;
  • if T is a reference type, no initialization is performed. 如果T是引用类型,则不执行初始化。

To default-initialize an object of type T means: 默认初始化 T类型的对象意味着:

  • if T is a non-POD class type (clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor); 如果T是非POD类类型(第9节),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的);
  • if T is an array type, each element is default-initialized; 如果T是数组类型,则每个元素都是默认初始化的;
  • otherwise, the object is zero-initialized. 否则,该对象被零初始化。

To value-initialize an object of type T means: 对值类型T的对象进行值初始化意味着:

  • if T is a class type (clause 9) with a user-declared constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor); 如果T是具有用户声明的构造函数(12.1)的类类型(第9节),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的);
  • if T is a non-union class type without a user-declared constructor, then every non-static data member and base-class component of T is value-initialized; 如果T是没有用户声明的构造函数的非联合类类型,那么T的每个非静态数据成员和基类组件都是值初始化的;
  • if T is an array type, then each element is value-initialized; 如果T是数组类型,那么每个元素都是值初始化的;
  • otherwise, the object is zero-initialized. 否则,该对象被零初始化。

Paragraph 7: 第7段:

An object whose initializer is an empty set of parentheses, ie, (), shall be value-initialized. 初始值为空集括号的对象,即(),应进行值初始化。

Paragraph 9: 第9段:

If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; 如果没有为对象指定初始化程序,并且该对象是(可能是cv限定的)非POD类类型(或其数组),则该对象应默认初始化; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor. 如果对象是const限定类型,则底层类类型应具有用户声明的默认构造函数。 Otherwise, if no initializer is specified for a nonstatic object, the object and its subobjects, if any, have an indeterminate initial value; 否则,如果没有为非静态对象指定初始化程序,则该对象及其子对象(如果有)具有不确定的初始值; if the object or any of its subobjects are of const-qualified type, the program is ill-formed. 如果对象或其任何子对象是const限定类型,则程序格式错误。

12 Special Member Functions [special] 12特别会员职能[特别]

Paragraph 7: 第7段:

An implicitly-declared default constructor for a class is implicitly defined when it is used to create an object of its class type (1.8). 当用于创建类类型(1.8)的对象时,将隐式定义类的隐式声明的默认构造函数。 The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with an empty mem-initializer-list (12.6.2) and an empty function body. 隐式定义的默认构造函数执行该类的初始化集合,该初始化集合将由用户编写的具有空mem-initializer-list(12.6.2)和空函数体的类的默认构造函数执行。

12.6.2 Initializing bases and members [class.base.init] 12.6.2初始化基数和成员[class.base.init]

Paragraph 4: 第4段:

If a given nonstatic data member or base class is not named by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer), then 如果给定的非静态数据成员或基类没有由mem-initializer-id命名(包括没有mem-initializer-list的情况,因为构造函数没有ctor-initializer),那么

  • If the entity is a nonstatic data member of (possibly cv-qualified) class type (or array thereof) or a base class, and the entity class is a non-POD class, the entity is default-initialized (8.5). 如果实体是(可能是cv限定的)类类型(或其数组)或基类的非静态数据成员,并且实体类是非POD类,则该实体是默认初始化的(8.5)。 If the entity is a nonstatic data member of a const-qualified type, the entity class shall have a user-declared default constructor. 如果实体是const限定类型的非静态数据成员,则实体类应具有用户声明的默认构造函数。
  • Otherwise, the entity is not initialized. 否则,实体未初始化。 If the entity is of const-qualified type or reference type, or of a (possibly cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of a const-qualified type, the program is ill-formed. 如果实体是const限定类型或引用类型,或者是(可能是cv限定的)POD类类型(或其数组)包含(直接或间接)const限定类型的成员,则该程序是错误的形成。

So now that the rules have been laid out, let's see how they apply: 现在已经制定了规则,让我们看看它们是如何应用的:

 temp temp1; 

temp is a non-POD type (because it has a std::string member), and since no initializer is specified for temp1 , it will be default-initialized (8.5/9). temp是非POD类型(因为它有一个std::string成员),并且由于没有为temp1指定初始化程序,因此它将被默认初始化(8.5 / 9)。 This calls the default constructor (8.5/5). 这会调用默认构造函数(8.5 / 5)。 temp has an implicit default constructor (12/7) which default-initializes the std::string member and the int member isn't initialized at all (12.6.2/4). temp有一个隐式默认构造函数(12/7),它默认初始化std::string成员, int成员根本没有初始化(12.6.2 / 4)。

 temp temp2 = temp(); 

On the other hand, the temporary temp object is value-initialized (8.5/7), which value-initializes all data members (8.5/5), which calls the default constructor in the std::string member and zero-initializes the int member (8.5/5). 另一方面,临时temp对象是值初始化的(8.5 / 7),它初始化所有数据成员(8.5 / 5),它调用std::string成员中的默认构造函数并对int零初始化成员(8.5 / 5)。

Of course, if you much rather not have to refer to the standard in 5+ different places, just ensure that you explicitly initialize everything (eg int i = 0; or using initializer lists). 当然,如果您不必在5个以上的不同位置引用标准,只需确保明确初始化所有内容(例如int i = 0;或使用初始化列表)。

The behavior of your code depends critically on the compiler you are using. 代码的行为主要取决于您使用的编译器。 More precisely, it depends on which version of language specification your compiler implements. 更准确地说,它取决于编译器实现的语言规范版本。

For C++98 compilers, both declarations have identical effect on the final values of the objects being declared: the str member should become empty, while the num members should contain unpredictable value. 对于C ++ 98编译器,两个声明对声明的对象的最终值具有相同的效果: str成员应该变为空,而num成员应该包含不可预测的值。 In both cases the actual initialization is default-initialization performed by a compiler-provided default constructor of class temp . 在这两种情况下,实际初始化都是由编译器提供的类temp默认构造函数执行的默认初始化 That default constructor initializes str , but leaves num uninitialized. 默认构造函数初始化str ,但是num未初始化。

For C++03 compilers the behavior is different. 对于C ++ 03编译器,行为是不同的。 There's no difference for temp1 object (its num is still unpredictable). temp1对象没有区别(它的num仍然是不可预测的)。 But temp2 initialization is handled differently. 但是temp2初始化的处理方式不同。 In C++03 the () initializer triggers the new kind of initialization - so called value-initialization . 在C ++ 03中, ()初始化程序触发了新的初始化 - 所谓的值初始化 Value-initialization ignores the compiler-provided default constructor of the top level object, and instead works directly on its subobjects (data members in this case). 值初始化忽略编译器提供的顶级对象的默认构造函数,而是直接在其子对象(本例中为数据成员)上工作。 So the temp2 object is effectively initialized by value-initialization, which also sets the num member to zero (in addition to initializing str with an empty string). 因此, temp2对象通过值初始化有效地初始化,这也将num成员设置为零(除了用空字符串初始化str )。 For this reason, temp2.num ends up being zero in C++03 compilers. 因此, temp2.num在C ++ 03编译器中最终为零。

If in your experiments you observed consistent zero in temp2.num , it means that your compiler follows the C++03 specification in this respect. 如果在您的实验中您在temp2.num观察到一致的零,则意味着您的编译器在这方面遵循C ++ 03规范。

temp temp1;

Will create a default initialized temp object. 将创建默认的初始化temp对象。 Since you provided no default constructor for temp , every member of temp will be default initialized too. 由于您没有为temp提供默认构造函数,因此每个temp成员也将默认初始化。 Since std::string provides a default ctor, it gets initialized correctly and has a well-defined value. 由于std::string提供了一个默认的ctor,它会被正确初始化并具有明确定义的值。 The integer, however, gets default initialized, which is implementation defined and normally a random value. 但是,整数默认初始化,这是实现定义的,通常是随机值。

temp temp2 = temp();

This will first create a value initialized temp object. 这将首先创建一个值初始化的temp对象。 This is important, because the object itself is value initialized, so are its members. 这很重要,因为对象本身是值初始化的,其成员也是如此。 It doesn't matter for the string, as default and value initialization are the same, but it matters for the integer. 对于字符串没有关系,因为默认值和值初始化是相同的,但它对整数很重要。 A value initialized integer has the value 0 . 值初始化整数的值为0
After that, you just copy over those members into temp2 . 之后,您只需将这些成员复制到temp2

Also, this relevant question might be of interest to you. 此外,您可能会对此相关问题感兴趣。
Edit : See my comment on @In silico's answer for explanation on why this isn't the case for MSVC. 编辑 :请参阅我对@In silico答案的评论,以解释为什么MSVC不是这种情况。 :/ :/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM