zoukankan      html  css  js  c++  java
  • 初始化列表的使用

    下列情况中, 为了让程序顺利编译, 必须使用 member initialization list:
    1. 初始化一个 reference member 时;
    2. 初始化一个 const member 时;
    3. 当调用一个 base class 的 constructor, 而它拥有一组参数时;
    4. 当调用一个 memeber class 的 constructor, 而它拥有一组参数时.

    考察以下代码:

    class Word
    {
        String _name;
        int    _cnt;
    public:
        //没有错误, 只是效率低
        Word()
        {
            _name = 0;    
            _cnt  = 0;
        }
    };

    这种情况下, Word constructor 会先产生一个暂时性的 String object, 然后将它初始化, 再以一个 assignment 运算符将暂时性的 object 指定给 _name, 然后再摧毁这个暂时性的 object, 以下是constructor 可能的内部扩张结果:

    //可能的结果
    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(): _name(0)
    {
            _cnt = 0;
    }
    
    //它会被扩张成这个样子
    //可能的代码
    Word::Word(/* this pointer goes here */)
    {
            //调用 String(int) constructor
            _name.String::String(0);
            _cnt = 0;
    }

    这又引起另外一个问题, 是否每个 member 都必须使用 member initialization list 来初始化呢?

    initialization list 不是一组函数, 编译器对于 initialization list , 会以适当次序在 constructor 之内安插初始化操作, 并且在任何 explicit user code 之前(例如之前 class Word 的初始化) 重点在于, 初始化的顺序是由 class 中的 members 声明的顺序决定, 不是由 initialization list 中的排列次序决定, 这就可能导致以下的问题:

    class X
    {
            int i;
            int j;
    public:
            //buggy, i(j) 会产生不确定行为
            X(int val):j(val), i(j)
            {}  
    };
    
    //较好的处理方式
    X::X(int val):j(val)
    { i = j; }

    还有一个有趣的问题, initialization list 中的项目被安插到 constructor 的函数体中, 会继续保存声明次序吗? 也就是说, 对于前一个代码, j 的初始化操作会安插在 explicit user assignment 操作 ( i = j ) 之前还是之后? 如果继续保存, 则这样操作也会导致不确定行为. 然而以上的代码是正确的, 因为 initialization list 的项目被放在 explicit user code 之前.

    另一个常见的问题是, 能否像下面那样, 调用一个 member function 以设定一个 member 的初值:

    //X::XFoo() 被调用
    X::X(int val): i(XFoo(val), j(val))
    {}

    答案是肯定的, 但是, 值得注意: 请使用 存在于 constructor 函数体内的一个 member, 而不要使用 存在于 initialization list 中的 member 设定初值. 因为你并不知道 XFoo() 对于 X object 的依赖程度有多高, 如果把 XFoo() 放在 constructor 体内, 那么对于到底是哪一个 member 在 XFoo() 执行时被设立初值这件事, 就不会导致模棱两可的情况. 

    用 member function 来初始化是合法的, 它会被编译器扩充为:

    // constructor 被扩充的结果
    X::X(/* this pointer */ int val)
    {
            i = this->XFoo(val);
            j = i;
    }

    那么如果一个 derived class member function 被调用, 其返回值被当作 base class constructor 的一个参数, 将会如何:

    class FooBar: public X
    {
        int _fval;
    public:
        int fval(){return _fval;}
        FooBar(int val): _fval(val), X(fval())    //使 fval() 作为 base class constructor 的参数
        {}
        ...
    };
    
    //这是个好主意吗?
    //这是可能的扩张结果
    FooBar::Foobar(/* this pointer goes here*/)
    {
        //编译器会把 initialization list 的代码扩展到 user code 的 前面
        X::X(this, this->fval());    //wtf, 导致不明确行为
        _fval = val;
    }
  • 相关阅读:
    【洛谷 P4542】 [ZJOI2011]营救皮卡丘(费用流)
    【洛谷 P2604】 [ZJOI2010]网络扩容(最大流,费用流)
    【洛谷 P3191】 [HNOI2007]紧急疏散EVACUATE(二分答案,最大流)
    【CF802C】 Heidi and Library (hard)(费用流)
    【洛谷 P4134】 [BJOI2012]连连看(费用流)
    【洛谷 P3965】 [TJOI2013]循环格(费用流)
    【洛谷 P3227】 [HNOI2013]切糕(最小割)
    【洛谷 P4072】 [SDOI2016]征途(斜率优化)
    【洛谷 P4437】 [HNOI/AHOI2018]排列(贪心,堆)
    【洛谷 P4291】 [HAOI2008]排名系统(Splay,Trie)
  • 原文地址:https://www.cnblogs.com/wuOverflow/p/4101494.html
Copyright © 2011-2022 走看看