成员初始化列表 初始化同赋值的区别

来源:互联网 发布:mac怎么切换应用程序 编辑:程序博客网 时间:2024/06/10 00:21
 成员初始化列表 初始化同赋值的区别 
1.我们可以认为构造函数的执行过程被分成两个阶段,隐式或显式初始化阶段以及一般的计算阶段。计算阶段由构造函数体内的所有语句构成,在计算阶段中数据成员的设置被认为是赋值而不是初始化。
    初始化阶段可以是显式的或隐式的,取决于是否存在成员初始化表。隐式初始化阶段按照声明的顺序,依次调用所有基类的缺省构造函数,然后是所有成员类对象的缺省构造函数。
例如当我们写如下代码 
inline Account:: Account() {   _name = "";   _balance = 0.0;   _acct_nmbr = 0; } 
则初始化阶段是隐式的在构造函数体被执行之前先调用与_name 相关联的缺省string构造函数,这意味着把空串赋给_name 的赋值操作是没有必要的。
 2.   对于非类数据成员的初始化或赋值,除了两个例外,两者在结果和性能上都是等价的。即,更受欢迎的实现是用成员切始化表
 //  更受欢迎的初始化风格 inline Account:: Account() : _balanae( 0.0 ), _acct_nmbr( 0 )  { } 
     两个例外是指任何类型的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和引用的初始化必须都已经发生。
 3.    每个成员在成员初始化表中只能出现一次,初始化的顺序不是由名字在初始化表中的顺序决定,而是由成员在类中被声明的顺序决定的。例如给出下面的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
 4.用一个类对象初始化该类另一个对象发生在下列程序情况下 
   <1> 用一个类对象显式地初始化另一个类对象例如 
Account newAcct( oldAcct ); 
    <2> 把一个类对象作为实参传递给一个函数例如 
extern bool cash_on_hand( Account acct ); if ( cash_on_hand( oldAcct ))  // ... 
     把一个类对象作为一个函数的返回值传递回来例如 
extern Account   consolidate_accts( const vector< Account >& ) {  Account final_acct;    // do the finances ...   return final_acct; } 
    <3> 非空顺序容器类型的定义例如 
//  五个 string  拷贝构造函数被调用 vector < string > svec( 5 ); 
     在本例中用string 缺省构造函数创建一个临时对象然后通过string 拷贝构造函数 
该临时对象被依次拷贝到vector 的五个元素中  
    <4> 把一个类对象插入到一个容器类型中例如 
svec.push_back( string( "pooh" )); 
--------以上摘自《C++ primer》
看下面的例子: ---摘自《高质量C++编程指南》
class A { …   A(void);        // 无参数构造函数   A(const A &other);   // 拷贝构 造函数   A & operate =( const A &other);  // 赋值函数 };    class B  {     public:     B(const A &a); // B 的构造函数     private:      A  m_a;     // 成员对象 };  
示例 a:
B::B(const A &a)  : m_a(a)       {      …  }
示例 b:
B::B(const A &a) { m_a = a; … }  

示例 b:示例 a 中,类 B 的构造函数在其初始化表里调用了类 A 的拷贝构造函数,从而
将成员对象 m_a 初始化。 
示例 b 中,类 B 的构造函数在函数体内用赋值的方式将成员对象 m_a 初始化。
我们看到的只是一条赋值语句,但实际上 B 的构造函数干了两件事:先暗地里创建 m_a
对象(调用了 A 的无参数构造函数),再调用类 A 的赋值函数,将参数 a 赋给 m_a。 


对于类成员,若它定义了带参数的构造函数后,而没有默认构造函数,那么该类对象就只能在成员初始化列表中进行初始化。

0 0