成员们的初始化队伍

必须使用member initialization list的四种情况:

  1. 当初始化一个reference member时;
  2. 当初始化一个const member时;
  3. 当调用一个base class的constructor,而它拥有一组参数时;
  4. 当调用一个member class的constructor,而它拥有一组参数时。
class Word {
    string _name;
    int _cnt;
public:
    Word() {
        _name = 0;
        _cnt = 0;
    }
}

Word constructor会先产生一个临时性的string object,然后将它初始化,之后以一个assignment运算符将临时性object指定给_name,随后再摧毁那个临时性object。
// C++伪码

Word::Word(/* this pointer goes here*/)
{
    // 调用string的default constructor
    _name.string::string();
    
    // 产生临时性对象
    string temp = string(0);
    
    // "memberwise"地拷贝_name
    _name.string::operator=(temp);
    
    // 摧毁临时性对象
    temp.string::~string();
    
    _cnt = 0;
}

修正后:

// 较佳的方式
Word::Word():string(0)
{
    _cnt = 0;
}

扩张的伪码:编译器会一一操作initialization list,以适当顺序在constructor之内安插初始化操作,并且在任何explicit user code之前。

// C++伪码
Word::Word(/* this pointer goes here*/)
{
    // 调用string constructor
    _name.string::string(0);
    _cnt = 0;
}

list中的项目顺序是由class中的members声明顺序决定的,不,会是由initialization list中的排列顺序决定的。
members声明顺序和initialization list中的排列顺序之间的外观错误会导致下面意想不到的危险。

class X {
    int i;
    int j;
public:
    X(val) :j(val), i(j)
    {
                
    }
}

代码看起来是把j设初值val,再把i设初值j。问题在于,由于声明顺序的缘故,initialization list中的i(j)其实比j(val)更早执行。但因j一开始未有初值,所以i(j)的执行结果导致i无法预知其值。修改为:

class X {
    int i;
    int j;
public:
    X(val) :j(val)
    {
        i = j;
    }
}

另一个常见的问题是,调用一个member function以设定一个member初值:

X::X(int val) : i(xfoo(val)), j(val) // xfoo(val)是X的一个member function
{
    
}

请使用“存在于constructor体内的一个member”,而不要使用“存在于member initialization list的member”,来为另一个member设定初值。你并不知道xfoo()放在constructor体内,那么对于“到底是哪一个member在xfoo()执行时被设立初值”这件事,就可以确保不会发生模棱两可的情况。
member的使用是合法的,这是因为和此object相关的this指针已经被构建妥当,而constructor大约被扩充为:
// c++伪码

X::X(/*this pointer*/int val)  // xfoo(val)是X的一个member function
{
    i = this->X::xfoo(val);
    j = val;
}

最后,如果一个derived class member function被调用,其返回值被当做base class constructor的一个参数,将会如何:
// 调用FooBar::fval()可以吗?

class Foobar  : public X {
    int _fval;
public:  
    int fval() { return _fval;}    
    FooBar(int val):_fval(val), X(fval())
    {
        
    }
};

扩张结果:

   FooBar(/*this pointer goes here*/)
    {
        // 实在不是一个好主意
        X::X(this, this->fval());
        _fval = val;
    }

简略地说,编译器会对initialization list 一一处理并可能重新排序,以反映出members的声明顺序。它会安插一些代码到constructor体内,并置于任何explicit user code之前。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值