三种情况下需要使用初始化成员列表 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
从上面可以看出隐式初始化的调用明显比成员初始化繁琐的多。