类数据成员的初始化,这份工作主要是构造函数负责的,构造函数的职责之一就是对数据成员进行正确初始化。实际上,构造函数的执行过程主要有两个阶段:
(1)初始化阶段
(2)普通计算阶段
初始化阶段是指对数据成员的初始化主要在构造函数的初始化列表中进行。普通计算阶段是指对数据成员的初始化是在构造函数的函数体中进行。因此,初始化阶段与普通计算阶段是两个不同的概念,需要予以区分。下面来看看什么是构造函数的初始化列表。
下面是一个时钟类Clock的定义:
class Clock
{
public:
Clock(int hour=0, int minute=0, int second = 0);
~Clock(){}
private:
int hour_;
int minute_;
int second_;
};
下面是Clock的构造函数实现:
Clock::Clock(int hour, int minute, int second): hour_(hour), minute_(minute), second_(second)
{
/* //这此称为普通计算段
hour_ = hour;
minute_ = minute;
second_ = second;
*/
cout << "Clock()..."<<endl;
}
上面代码中的“:”号后面的代码就是构造函数的初始化列表,也就是构造函数执行的第一阶段,利用构造函数的初始化列表来对数据成员的初始化其效率是更高的,其实在函数体内执行的代码在某种意义上来说,不能称为初始化,是赋值操作,是构造函数的普通计算段,它的效率更低。构造函数的初始化列表中,如果有多个数据成员,成员之间则以逗号分隔。
我们这里说的数据成员,包含普通数据类型的数据成员,如int,double, char等,还有类对象的数据成员,const数据成员以及引用数据成员。对数据成员的初始化我们建议在构造函数的初始化列表中。而对于下面几种情形而言是必须要在构造函数的初始化列表中进行的:
(1) 类对象成员,当它所对应的类没有默认构造函数时,只能在构造函数的初始化列表中进行(如果有默认构造函数,虽然可以不在初始化列表中进行,考虑到效率的问题,不建议在普通计算段中初始化)
(2) const成员,其初始化也只能在构造函数的初始化列表中进行
(3) 引用成员, 其初始化也只能在构造函数的初始化列表中进行
声明一下:数据成员的初始化顺序,与构造函数的初始化列表中的顺序无关,与在类中声明的成员顺序有关。
下面是示例:
class Object
{
public:
enum E_TYPE
{
TYPE_A = 100,
TYPE_B
};
public:
Object(int num = 0): num_(num), kNum_(num), refNum(num_) //主要关注这行初始化列表。
{
cout << "Object..."<<endl;
}
private:
int num_;
const int kNum_; //const 成员
int & refNum_; //引用成员
};
int main()
{
Object obj1(10);
Object obj2(20);
//对任何对象而言TYPE_A,TYPE_B都是常量
cout << Object::TYPE_A << endl;
cout << obj1::TYPE_A <<endl;
cout << obj2::TYPE_A << endl;
cout << Object::TYPE_B << endl;
cout << obj1::TYPE_B << endl;
cout << obj2::TYPE_B << endl;
return 0;
}
这里需要指出的是,对于const成员,不同的对象有不同的const值,obj1的kNum为100,obj2的kNum为200,如果想让所有对象均有一个共同的常量值,不能用const,而应该使用枚举型,如上述代码中的TYPE_A, TYPE_B;