初始化列表
初始化列表是用于构造函数参数的初始化,与其他函数不同,构造函数除了有名字,参数列表和函数体之外,还可以有初始化列表,初始化列表以冒号开头,后跟一系列以逗号分隔的初始化字段。
如:
class myClock
{
public:
myClock(int hh,int mm,int ss); //构造函数的声明
private:
int hour,minu,second;
};
//构造函数的实现和初始化设定
myClock::myClock(int hh,int mm,int ss):hour(hh),minu(mm),second(ss)
{
cout<<"初始化完毕"<<endl;
}
如上所示,在构造函数的实现部分,我们用了初始化列表来初始化myClock类内的三个数据成员hour,minu,second。
后面:号跟的hour(hh),minu(mm),second(ss)这三个其实就等价于:
hour = hh;
minu = mm;
second = ss;
因此,就等价于执行了一个赋值操作,也就是初始化操作。
所以,我们构造函数的实现和初始化设定也可以这么写:
class myClock
{
public:
myClock(int hh,int mm,int ss); //构造函数的声明
private:
int hour,minu,second;
};
//构造函数的实现和初始化设定
myClock::myClock(int hh,int mm,int ss)
{
hour = hh;
minu = mm;
second = ss;
cout<<"初始化完毕"<<endl;
}
其实现的功能与之前完全一模一样,不过效率相比初始化列表差了点。因此主要是性能问题,当然,对于内置类型,如int, float等,使用初始化类表和在构造函数体内初始化差别不是很大,但是对于内嵌类的类型来说,最好使用初始化列表,为什么呢?因为使用初始化列表少了一次调用默认构造函数的过程,这对于数据密集型的类来说,是非常高效的。
组合类的构造函数和复制构造函数
经过以上分析,我们知道,对组合类(内嵌类)最好使用初始化列表初始化,因此在书中特意提到了要使用初始化列表来调用构造函数和复制构造函数进行初始化,但对普通类的构造函数和复制构造函数均没有说明。刚开始还以为是因为组合类的构造函数和复制构造函数和普通类不同,因此略有疑惑,但查阅资料后发现,普通类和组合类的构造函数和复制构造函数声明形式、实现形式是完全相同的。只不过组合类的构造函数和复制构造函数其初始化赋值时运用了初始化列表,仅此而已。
因此书中写到:
组合类的构造函数定义的一般形式为:
//组合类的构造函数
类名::类名(形参表):内嵌对象1(形参表1),内嵌对象2(形参表2),......
{
函数体内部;
}
组合类的复制构造函数定义的一般形式为:
//组合类的复制构造函数
类名::类名(类名 &命名的对象名):内嵌对象1(对象名.内嵌对象1),内嵌对象2(对象名.内嵌对象2)......
{
函数体内部;
}
这两个函数的定义在冒号前的部分和普通类是完全相同的,从:号开始,是运用了初始化列表,所以看起来和普通类不同而已,其实普通类和组合类的构造函数和复制构造函数形式是一样的。
上述定义其实就是:
//组合类的构造函数
类名::类名(形参表)
{
内嵌对象1 = 形参表1;
内嵌对象1 = 形参表2;
......
函数体内部;
}
//组合类的复制构造函数
类名::类名(类名 &命名的对象名)
{
内嵌对象1 = 对象名.内嵌对象1
内嵌对象2 = 对象名.内嵌对象2
......
函数体内部;
}
理解了初始化列表,也就理解了书上所说的组合类的构造函数和复制构造函数的实现形式的奇怪之处,事实上,除了性能问题之外,有些时候合初始化列表是不可或缺的。
以下几种情况时必须使用初始化列表:
1.常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面。
2.引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面。
3. 没有默认构造函数的内嵌类的类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用复制构造函数初始化。