在对类成员变量初始化的时候,我们很多时候没有注意到,在类构造函数的初始化列表中初始化成员变量,和在构造函数中赋值初始化成员变量的差别是很大的,不仅表现在程序的执行效率上,同时还有语法上的区别。类定义如下:
1: class String
2: {
3: public:
4: String()
5: {
6: cout << "String::String()" << endl;
7: }
8: String(String& str)
9: {
10: m_value = str.m_value;
11: cout << "String::String(String& str)" << endl;
12: }
13: ~String(){}
14: String& operator=(String& src)
15: {
16: m_value = src.m_value;
17: cout << "String::operator =" << endl;
18: return *this;
19: }
20: protected:
21: int m_value;
22: };
23:
24: class Base
25: {
26: public:
27: Base(String& str);
28: Base(String& str, int flag);
29: ~Base(){}
30: protected:
31: String m_string;
32: const int m_flag;
33: };
34:
35: Base::Base(String& str)
36: {
37: m_string = str;
38: }
39:
40: Base::Base(String& str, int flag):m_string(str), m_flag(flag)
41: {
42: }
Base类包含了两种类型的构造函数,分别是在构造函数中初始化成员变量以及在初始化列表中初始化成员变量。下面的代码是体现出两种方式的差别:
1、在构造函数中初始化话成员变量
1: int main(int argc, int *argv[])
2: {
3: String str;
4:
5: Base base1(str);
6:
7: getchar();
8: return 0;
9: }
程序执行结果如下:
第一条输出是String str时调用构造函数产生的,这样base1对象的初始化会调用String的默认构造函数和String的=操作符函数
2、在初始化列表中初始化成员变量
1: int main(int argc, int *argv[])
2: {
3: String str;
4:
5: Base base2(str, 1);
6:
7: getchar();
8: return 0;
9: }
程序执行结果如下:
从结果可以看出,两种不同的方式,执行的过程差别很大,前一种方式比后一种要多执行一个String的=操作符函数,所以在初始化列表中初始化成员变量的方式更加合理。
从C++语法上比较,成员变量m_flag是const int类型,它的初始化过程只能在构造函数的初始化列表中赋值,不能在构造函数中赋值。严格来说,对象的数据成员初始化过程已经在调用构造函数之前完成了,在构造函数中“初始化”,只是保证成员变量在别处引用之前给它一个确定的初值。
在程序的世界里,任何规则都不是绝对的合理,对于类中大量的固定类型的数据成员的初始化,比如这样
1: private:
2: int a, b, c, d, e, f, g, h;
3: double i, j, k, l, m;
这个时候在构造函数中赋值,比在初始化列表中初始化跟合理,把这个代码放在构造函数体里面赋值,看上去代码会更加赏心悦目,同时也不会导致额外的效率问题。只是很简单的问题,写下来是为了让自己理解更加透彻,能正确遵守这个规则,有不对之处,欢迎指出。