C++构造函数与赋值函数

关于编译器合成的构造函数

  • 本文从编译器的角度理解一些关于编译器默认合成的构造函数及对象初始化的一些细节,这些细节容易被忽略或者被误解。

Default constructor

  • 误解一:任何class如果程序员没有显式定义default constructor,则编译器就会合成一个。
    default constructor只会在需要的时候才由编译器合成,关于“需要的时候”的解释如下:这里的主体是编译器,即在编译器需要的时候,被合成出来的constructor只执行编译器所需要的动作。在没有显式定义时,以下四种情况下会合成 default constructor:

    • 带有default constructor的member class object;
    • 带有default constructor的base class;
    • 带有一个virtual function的class;
    • 带有一个virtual base class的class。

    至于没有存在这4种情况而又没有显式声明任何constructor的class,我们说它们拥有的是implicit trivial default constructors,它们实际上并不会被编译器合出来。

  • 误解二:编译器合成的default constructor会显式设定class内每一个数据成员(data member)的默认值。
    在合成的default constructor中,只有base class subobjects和member class objects会被初始化,所有其他的nonstatic data member(例如整数,整数指针,整数数组等)都不会被初始化。这些初始化操作作为对程序而言或许有必要,但对编译器来说是非必要的。

Copy Constructor

  • 以下三种情况需要调用copy constructor:

    • 一个对象以值传递的方式传入函数体;
    • 一个对象以值传递的方式从函数返回;
    • 一个对象显式地使用另外一个对象进行初始化。

    同样,如果没有显式定义,编译器就会在“需要的时候”合成copy constructor。copy constructor同样区分为trivial和nontrivial两种,只有nontrivial的实例(这里指代copy constructor)才会被实际上被合成出来,决定一个copy constructor是否为trivial的标准在于class是否展现出“bitwise copy semantics”(位逐次拷贝),即如果“bitwise copy semantics”已经能成功且正确地完成copy任务,则编译器无需再合成copy constructor,即为trivial。(这里的主体是编译器,并不是程序,比如如果class成员包含指针,使用bitwise copy semantics的copy对程序来说可能会造成不同对象实例的成员指针引用同一块内存,会有double free等问题,但这和编译器是否会合成copy constructor没有关系)

  • 没有显式定义时,以下四种情况下,class 不展现“bitwise copy semantics”(即编译器使用bitwise copy semantics不能成功完成copy任务):

    • 当一个class内含一个member object 而后者的class声明有一个copy constructor时(不论是由class设计者显式声明还是由编译器隐式合成出来);
    • 当class继承自一个base class 而后者存在一个copy constructor(不论是由class设计者显式声明还是由编译器隐式合成出来);
    • 当class声明了一个或多个virtual functions时;
    • 当一个class派生自一个继承链,其中有一个或多个virtual base classes时。