类对象的构造顺序是这样的:
1.分配内存,调用构造函数时,隐式/显示的初始化各数据成员
初始化阶段可以是显式的或隐式的,取决于是否存在成员初始化表。隐式初始化阶段按照声明的顺序依次调用所有基类的缺省构造函数,然后是所有成员类对象的缺省构造函数。
2.进入构造函数后在构造函数中执行一般计算
计算阶段由构造函数体内的所有语句构成。在计算阶段中,数据成员的设置被认为是赋值,而不是初始化。
使用初始化列表有两个原因:
1.必须这样做:
三种情况下需要使用初始化成员列表
1)对象成员;
2)const修饰的成员;
3)引用成员数据;
(1)如果有一个类成员,它本身是一个类或者是一个结构,而且这个成员它只有一个带参数的构造函数,而没有默认构造函数,这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数,如果没有初始化列表,那么他将无法完成第一步,就会报错。
class ABC
{
public :
ABC( int x, int y, int z):a(x),b(y),c(z) {} ;
private :
int a;
int b;
int c;
} ;
class MyClass
{
public :
MyClass( int a, int b, int c):abc(a,b,c) {}
private :
ABC abc;
} ;
int main()
{
MyClass o( 1 , 2 , 3 );
return 0 ;
}
(2)当类成员中含有一个const成员时
(3)当类成员中含有一个引用时
using namespace std;
class ConstRef {
public :
ConstRef( int i);
void print();
private :
int a;
const int b; // const成员
int & c; // 引用
} ;
ConstRef::ConstRef( int i):b(i),c(a) // 含有一个const对象时,或者是一个引用时使用初始化成员列表
{
a = i; // ok
// b = i; // 错误
// c = a; // 错误
}
void ConstRef::print()
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
int main()
{
ConstRef o( 1 );
o.print();
return 0 ;
}
2.效率要求这样做:
类对象的构造顺序显示,进入构造函数体后,进行的是计算,是对他们的赋值操作,显然,赋值和初始化是不同的,这样就体现出了效率差异,如果不用成员 初始化列表,那么类对自己的类成员分别进行的是一次隐式的默认构造函数的调用,和一次复制操作符的调用,如果是类对象,这样做效率就得不到保障。
注意:构造函数需要初始化的数据成员,不论是否显式的出现在构造函数的成员初始化列表中,都会在该处完成初始化,并且初始化的顺序和其在声明时的顺序是一致的,与列表的先后顺序无关,所以要特别注意,保证两者顺序一致才能真正保证其效率。
现在明白为什么要使用成员初始化列表了。
这里再强调一下类的初始化的顺序,应该是类成员变量的初始化不是按照初始化表的顺序被初始化的,而是按照在类中声明的顺序被初始化的。
这是摘自:Effective C++学习笔记:初始化列表中成员列出的顺序和它们在类中声明的顺序相同 http://www.cppblog.com/xczhang/archive/2008/01/22/41613.html
为什么会这样呢?我们知道,对一个对象的所有成员来说,它们的析构函数被调用的顺序总是和它们在构造函数里被创建的顺序相反。那么,如果允许上面的 情况(即,成员按它们在初始化列表上出现的顺序被初始化)发生,编译器就要为每一个对象跟踪其成员初始化的顺序,以保证它们的析构函数以正确的顺序被调 用。这会带来昂贵的开销。所以,为了避免这一开销,同一种类型的所有对象在创建(构造)和摧毁(析构)过程中对成员的处理顺序都是相同的,而不管成员在初 始化列表中的顺序如何。
注意:上述内容不适用于static变量,static变量应该在类的构造函数前被初始化。