三种情况下需要使用初始化成员列表
1)对象成员;
2)const修饰的成员;
3)引用成员数据;
1.对于非类数据成员的初始化或赋值,除了两个例外,两者在结果和性能上都是等价的。
两个例外是指任何类型的const和引用数据成员。const 和引用数据成员也必须是在成员初始化表中被初始化 否则 就会产生编译时刻错误。
例如 下列构造函数的实现将导致编译 时刻错误:
class ConstRef {
public:
ConstRef( int ii );
private:
int i;
const int ci;
int &ri;
};
ConstRef::
ConstRef( int ii )
{ // 赋值
i = ii; // ok
ci = ii; // 错误: 不能给一个 const 赋值
ri = i; // 错误 ri 没有被初始化
}
当构造函数体开始执行时 所有const 和引用的初始化必须都已经发生。因此 只有将它 们在成员初始化表中指定这才有可能 正确的实现如下:
// ok: 初始化引用和 const
ConstRef::
ConstRef( int ii )
: ci( ii ), ri( i )
{ i = ii; }
2. 每个成员在成员初始化表中只能出现一次 初始化的顺序不是由名字在初始化表中的顺序决定 而是由成员在类中被声明的顺序决定的. 例如, 给出下面的 Account 数据成员的声明顺序 :
class Account {
public:
// ...
private:
unsigned int _acct_nmbr;
double _balance;
string _name;
};
下面的缺省构造函数
inline Account::
Account() : _name( string() ), _balance( 0.0 ), _acct_nmbr( 0 )
{}
的初始化顺序为 acct_nmbr _balance 然后是_name.但是在初始化表中出现(或者在被隐式初始化的成员类对象中) 的成员,总是在构造函数体内成员的赋值之前被初始化. 例 如 在下面的构造函数中
inline Account::
Account( const char *name, double bal )
: _name( name ), _balance( bal )
{
_acct_nmbr = get_unique_acct_nmbr();
}
初始化的顺序是_balance _name 然后是_acct_nmbr
由于这种 实际的初始化顺序 与 初始化表内的顺序 之间的明显不一致,有可能导致以下难于发现的错误,当用一个类成员初始化另一个时:
class X {
int i;
int j;
public:
// 喔! 你看到问题了吗?
X( int val )
: j( val ), i( j )
{}
// ...
};
尽管看起来 j 好像是用 val 初始化的 而且发生在它被用来初始化i 之前 但实际上是i 先被初始化的, 因此它是用一个还没有被初始化的 j 初始化的 我们的建议是 把“用一个成员对另一个成员进行初始化 如果你真的认为有必要” 的代码放到构造函数体内, 如下所示 :
// 更好的习惯用法
X::X( int val ) : i( val ) { j = i; }
--------摘自《C++ Primer》
看下面的代码:
class A
{
public:
A();
A(int aValue);
A(const A& aObject);
A& operator=(const A& aObject);
private:
int iValue;
};
A::A()
{
cout << "default construct function is called" << endl;
}
A::A(int aValue)
{
iValue = aValue;
cout << "construct function is called" << endl;
}
A::A(const A& aObject)
{
this->iValue = aObject.iValue;
cout << "copy construct function is called" << endl;
}
A& A::operator =(const A &a)
{
this->iValue = a.iValue;
cout << "copy assignment function is called" << endl;
return *this;
}
class B
{
public:
B(int aValue);
private:
int iValue;
A a;
};
B::B(int aValue)
{
a = 5;
}
class C
{
public:
C(int aValue);
private:
int iValue;
A a;
};
C::C(int aValue):a(5)
{
cout << "construct function of the C object is called" << endl;
}
int main(int argc, char* argv[])
{
B b(5);
cout << "-------------------" << endl;
C c(5);
delete p;
delete pp;
return 0;
}
程序会输出:
default construct function is called
construct function is called
copy assignment function is called
-------------------
construct function is called
construct function of the C object is called
从上面可以看出隐式初始化的调用明显比成员初始化繁琐的多。