在初始化类的成员的时候,我们经常会有两种选择,其一是类构造函数的成员初始化列表,其二是构造函数的函数体。那么这两者的区别又是什么,成员初始化列表的具体行为到底是什么呢?
类对象的构造顺序是这样的:
1.分配内存,调用构造函数时,隐式/显示的初始化各数据成员;
2.进入构造函数后在构造函数中执行一般赋值与计算。
性能上的区别
看下面一段代码:
inline String::String() { cout<<"String的默认构造函数"<<endl; ... } inline String::String(const char *s) { printf("String(const char *s)的构造函数,s=%s ",s); ... } class testclass{ public: testclass(); testclass(char *a,int b); private: String _name; char _b; }; testclass::testclass() { printf("默认构造函数 "); } testclass::testclass(char *a, int b) { cout<<"testclass的构造函数"<<endl; _name=a; _b=b; } testclass("abc",12); //结果为: //String的默认构造函数 //testclass的构造函数
//String(const char *s)的构造函数,s=abc
这种初始化方式是在构造函数体内给成员变量赋值。有日志可以看出初始化过程中发生了如下几件事情:
1、首先调用String的default构造函数给_name赋初值
2、然后进入testclass的构造函数体内
3、立刻再对_name赋予新值
再看下一段代码,用初始化成员列表的形式进行初始化:
testclass::testclass(char *a, int b):_name(a),_b(b) { cout<<"testclass的构造函数"<<endl; } //结果为: //String(const char *s)的构造函数,s=abc //testclass的构造函数
可见第一种方式(即在构造函数内部赋值)虽然效果一样,但是多调用了一次default构造函数,有点浪费了,特别是当一个类中成员很多,且多为自定义的类类型时,资源就更浪费了。使用初始化成员列表的方式避免了这一浪费,直接调用string copy构造函数(不一定是copy 构造函数),更加高效。还有其他原因,待研究。。。
功能上的区别
除了性能上面的差别,有几种情况下必须使用初始化成员列表而非函数体内赋值来初始化变量:
1、初始化引用类型的成员变量;
2、初始化const类型的成员变量;
3、当在初始化一个子类的时候,其父类没有默认构造函数,即不带参数的构造函数,只有显示含有参数的构造函数;
4、类含有类成员变量(包括继承的和本身的)
对于1、2两点比较好理解,因为const、引用类型不接受定义之后的赋值。
对于3的话,我们调用子类的构造函数之前,会先调用父类的构造函数。如果父类只有带参数的构造函数,那就会调用带参数的构造函数,这时候就必须使用初始化成员列表传入参数给父类的带参数的构造函数。否则无法完成父类的初始化,编译会报错。
对于4,如果我们有一个类成员,它本身是一个类或者是一个结构,而且这个成员它只有一个带参数的构造函数,而没有默认构造函数,这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数,如果没有初始化列表,那么他将无法完成第一步,就会报错。