构造函数初始化数据成员:在构造体内初始化数据成员,不是真正意义上的初始化,而是赋值。真正地初始化时使用”初始化表达式表“(简称初始化列表进行的)。
初始化列表位于构造函数参数表之后,在函数体{}之前。这说明该列表里的初始化工作发生在函数体内的任何代码被执行之前。
构造函数的初始化列表的使用规则:
(1)如果类存在继承关系,派生类可以直接在其初始化列表里调用基类的特定构造函数以向它传递参数,因为我们不能在初始化对象时访问基类的数据成员。如下示例:
(2)类的非静态const数据成员和引用成员只能在初始化列表里初始化,因为它们只存在初始化语义,而不存在赋值语义;
(3)类的数据成员的初始化可以采用初始化列表或函数体内赋值两种方式,这两种方式的效率不完全相同
class A
{
A(void); //默认构造函数
A(const A& other); //拷贝构造函数
A& operator =(const A& other); //赋值函数
}
class B
{
public :
B(const A & a); //B的构造函数
private:
A m_a; //成员对象
}
//(1)采用初始化列表的方式初始化
B::B(const A &a):m_a(a)
{
...
}
//(2)采用函数体内赋值的方式初始化
B::B(const A& a)
{
m_a = a;
...
}
本例中第一种方式,类B的构造函数在其初始化列表里调用了A的拷贝构造函数,从而将成员对象m_a初始化。
第二种方式,类B的构造函数在函数体内用赋值的方式将成员对象m_a初始化。虽然只是一条赋值语句,但B的构造函数干了两件事:先暗地里创建m_a对象(调用了A的默认构造函数),在调用类A的赋值函数,才将参数a赋给m_a。
第一种方式效率比第二种高,对于内部数据类型的数据成员而言,两种初始化方式效率几乎没有区别,但第二种方式的程序版式更清晰
如下所示:
class F
{
public:
F(int x, int y); //构造函数
private:
int m_x;
int m_y;
};
//(1)采用初始化列表的方式初始化
F::F(int x, int y):m_x(x), m_y(y)
{
}
//(2)采用函数体内赋值的方式初始化
F::F(int x, int y)
{
m_x = x;
m_y = y;
}
提示:使用成员初始化列表初始化数据成员时,成员真正地初始化顺序并不一定与你在初始化列表中为他们安排的顺序一致,编译器总是按照它们在类中声明的次序来初始化。因此,最好是按照它们的声明顺序来书写成员初始化列表:
(1)调用基类的构造函数,向他们传递参数;
(2)初始化本类的数据成员(包括成员对象的初始化);
(3)在函数体内完成其他的初始化工作。
主要三种情况需要使用列表初始化
(1)初始化对象;
(2)初始化const类型变量;
(3)初始化引用。