一个对象的构造分两部分,首先是分配空间,然后初始化。
只要有对象生成,不管是以什么形式生成,都会调用构造函数进行初始化。
然后下面有个例子,在蓝色区域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或引用类型数据成员的唯一机会是在构造函数初始化列表中。)
成员被初始化的次序就是声明成员的次序。与构造函数初始化列表无关。(消亡顺序和声明顺序相反,忘了哪看的了。)
当用于类类型对象时,初始化的复制形式和直接形式有所不同。直接初始化直接调用与实参匹配的构造函数,复制初始化总是调用复制构造函数。复制初始化首先使用指定构造函数创建一个临时对象,然后用复制构造函数将那个临时对象复制到正在创建的对象。所以,直接初始化效率高些。
定义,用于为变量分配存储空间,还可以为变量指定初始值。变量有且仅有一次定义。
声明,用于向程序表明变量的类型和名字(标识符)。变量可以声明多次。
定义也是声明,当定义变量时我们声明了它的类型和名字。
可以通过使用extern关键字声明变量而不定义它。extern声明不是定义,也不分配存储空间,它只是说明变量定义在程序的其他地方(文件)。
只有当声明也是定义时,声明才可以有初始化式,因为只有定义才分配存储空间。初始化式必须要有存储空间来进行初始化。如果声明有初始化式,那么它可被当作是定义,即使声明标记为extern。
且只有当extern声明位于函数外部时,才可以含有初始化式。
原文地址:http://blog.csdn.net/buxizhizhou530/article/details/45251879