C++ 初始化形式、变量初始化规则、类构造函数的初始化列表

时间:2022-09-09 17:31:18

类构造函数的初始化列表,举例

一个对象的构造分两部分,首先是分配空间,然后初始化。

只要有对象生成,不管是以什么形式生成,都会调用构造函数进行初始化。

然后下面有个例子,在蓝色区域Big类的复制构造函数中,使用初始化列表进行成员的初始化(方法1)没有问题,而如果不使用初始化列表、直接在函数里用里面注释掉的代码(方法2)则会报错:Base类没有合适的构造函数。

// W3-课程作业2-4.cpp : Defines the entry point for the console application.
#include "stdafx.h"
//下面程序的输出结果是:
//5, 5
//5, 5
//请填空:

#include <iostream>
using namespace std;
class Base {
public:
int k;
Base(int n) :k(n) { }
};
class Big {
public:
int v; Base b;
// 在此处补充你的代码
//Big ________________{}
//Big ________________{}
Big(int n) :v(n), b(n) {}

<span style="color:#3333ff;">Big(const Big& a) :v(a.v), b(a.b)
{
//v = a.v;
//b = a.b;
}</span>
};

int _tmain(int argc, _TCHAR* argv[])
{
Big a1(5);
Big a2 = a1;
cout << a1.v << "," << a1.b.k << endl;
cout << a2.v << "," << a2.b.k << endl;
system("pause");
return 0;
}
错误的原因如下。

构造一个对象时,先要构造这个对象的每一个属性,然后再执行该对象构造函数内部的语句。
初始化列表,其实是定义了如何构造每个属性。而初始化列表中没有列出的属性值,则是按照默认的方法初始化。
因此,对于这个例子而言:
方法1使用初始化列表:采用初始化列表中的b(a.b)构造Base类对象b,即调用了合成的复制构造函数,没有问题;
方法2不使用初始化列表:由于b的构造方法并没有在初始化列表中说明,因此在执行构造函数之前,需要先调用无参构造函数产生一个Base对象b,而这个无参构造函数并不存在,从而产生错误。

另外,之前以为方法2那样,执行b=a.b;时会调用合成的复制构造函数(即使是这样,b也还是没有合适的构造函数初始化。这条语句的时候已经是赋值,而不是初始化),事实上这句是赋值,调用的当然不是复制构造函数,而是执行合成赋值操作符(与合成复制构造函数类似)。这里把概念弄清楚,以后会少犯混的。


事实上,调用复制构造函数有三种情形:(而且复制构造函数本身是构造函数的一种,用来对对象初始化)

  • 定义一个新对象,并用一个同类型的对象对它进行初始化时(显示使用复制构造函数)
  • 当将该类型的对象传递给函数,即给形参初始化(隐式使用复制构造函数)
  • 当从函数返回该类型的对象时,即给返回值对象初始化(隐式使用复制构造函数)
  • (另)初始化顺序容器中的元素
  • (另)根据元素初始化式列表初始化数组元素

派生类对象生成时,要先调用基类的构造函数。有两种方式调用:

  • 显示方式。即在初始化列表中,指明 成员基类对象(基类对象) 这样的方式调用基类的构造函数
  • 隐式方式。即不指明,自动得调用基类的默认构造函数。(你要确保基类有默认构造函数,否则出错)

初始化列表

必须对任何 const 或引用类型成员、以及没有默认构造函数的类类型的成员使用初始化列表。
(记住,可以初始化const对象或引用类型的对象,但不能对它们赋值。在开始执行构造函数的函数体之前,要完成初始化。
初始化const或引用类型数据成员的唯一机会是在构造函数初始化列表中。)


成员初始化的次序

成员被初始化的次序就是声明成员的次序。与构造函数初始化列表无关。(消亡顺序和声明顺序相反,忘了哪看的了。)

这是因为,后声明的变量有可能依赖先声明的成员变量,因此要有先声明先初始化,后声明的后初始化参考此

C++ 支持两种形式的初始化

  • 复制初始化。用等号。
  • 直接初始化。把初始化式放在括号中。
初始化不是赋值。初始化指创建变量并给它赋初始值,而赋值则是擦除对象的当前值并用新值代替。

当用于类类型对象时,初始化的复制形式和直接形式有所不同。直接初始化直接调用与实参匹配的构造函数,复制初始化总是调用复制构造函数。复制初始化首先使用指定构造函数创建一个临时对象,然后用复制构造函数将那个临时对象复制到正在创建的对象。所以,直接初始化效率高些。

变量初始化规则

  • 内置类型变量的初始化
全局变量初始化为0,局部变量初始化不进行自动初始化,我觉得静态变量应该也是初始化为0
  • 类类型变量的初始化

C++ 区分了声明和定义

定义,用于为变量分配存储空间,还可以为变量指定初始值。变量有且仅有一次定义。

声明,用于向程序表明变量的类型和名字(标识符)。变量可以声明多次。
定义也是声明,当定义变量时我们声明了它的类型和名字。
可以通过使用extern关键字声明变量而不定义它。extern声明不是定义,也不分配存储空间,它只是说明变量定义在程序的其他地方(文件)。

只有当声明也是定义时,声明才可以有初始化式,因为只有定义才分配存储空间。初始化式必须要有存储空间来进行初始化。如果声明有初始化式,那么它可被当作是定义,即使声明标记为extern。

且只有当extern声明位于函数外部时,才可以含有初始化式。