• C++ 初始化列表(Initialization List)


      请注意以下继承体系中各class的constructors写法:

    复制代码
     1 class CPoint
     2 {
     3 public:
     4     CPoint(float x=0.0)
     5     :_x(x){}
     6     
     7     float x() {return _x;}
     8     void x(float xval){_x=xval;}
     9 protected:
    10     float _x;
    11 };
    12 
    13 class CPoint2d:public CPoint{
    14 
    15 public:
    16     CPoint2d(float x=0.0,float y=0.0)
    17     :CPoint(x),_y(y){}
    18 
    19    float y(){return _y;}
    20    void y(float yval){_y=yval;}
    21 protected:
    22     float _y;
    23 };
    24 
    25 class CPoint3d:public CPoint2d{
    26 public:
    27     CPoint3d(float x=0.0,float y=0.0,float z=0.0)
    28      :CPoint2d(x,y),_z(z){}
    29      
    30     float z(){return _z;}
    31     void z(float zval){_z=zval;}
    32 
    33 protected:
    34     float _z;
    35 
    36 
    37 };
    复制代码

      在constructor声明之后有一个:符号,后面紧跟着一个(以上)的函数调用动作,这一行就是所谓的initialization list。它的作用是在进入constructor主体动作之前,,先唤起其中所列的函数。例如上面的:

      第5行表示:在执行CPoint::CPoint(x)之前,先执行_x(x); (注:语言内建类型如int、float、long等等也是一种class,因为变量_x的类型是float,所以_x(x)的意思是启动“float class”的constrnctor,也就把_x的初值设为x;

      第17行表示:执行CPoint2d::CPoint2d(x,y)之前,先执行CPoint(x)和_y(y).

      第28行表示:执行CPoint3d::CPoint3d(x,y,z)之前,先执行CPoint2d(x,y)和_z(z).

      因此当我产生一个CPoint3d object如下:

    CPoint3d aPoint3d(1.1, 2.2, 3.3);

      会有以下六个动作依序被调用:

    复制代码
    _x(1.1); // 相当于 _x = 1.1;
    CPoint::CPoint(1.1); // 本例沒做什么事
    _y(2.2); // 相当于 _y = 2.2;
    CPoint2d::CPoint2d(1.1, 2.2); // 本例沒做什么事
    _z(3.3); // 相当于_z = 3.3;
    CPoint3d::CPoint3d(1.1, 2.2, 3.3); // 本例沒做什么事
    复制代码

      你可能会问,既然继承体系中的建构方式是由内而外,由上而下,那么这里产生个CPoint3d object,必然会调用CPoint2d和CPoint的constrnctor,而所有初始化动作都可以在其中完成,initialization list的出现会不会是显得多此一举?做个测试就知道了,把上一段27行的代码改为这样试试:

    CPoint3d( float x = 0.0, float y = 0.0, float z = 0.0 ) { _z = z; }

      其中没有指定initialzation list。结果竟然无法通过编译:

    error C2668: 'CPoint2d::CPoint2d' : ambiguous call to overloaded
    function

      也就是说,当编译器根据继承体系往上一层调用base class constructor时,发现CPoint2d有两个constructors,而它不知道应该调用哪一个。这就是initialization list最明显的存在的价值。如果本例的CPoint2d只有一个constructor,像这样:

    复制代码
    1 class CPoint2d : public CPoint {
    2 public:
    3   CPoint2d( ) { _y = 0.0; } // default constructor
    4 protected:
    5   float _y;
    6 };
    复制代码

      或者这样

    复制代码
    1 class CPoint2d : public CPoint {
    2 public:
    3     CPoint2d( float x = 0.0, float y = 0.0 )
    4     : CPoint( x ), _y( y ) { }
    5 protected:
    6     float _y;
    7 };
    复制代码

      而 CPoint3d constructor 中沒有列出 initialization list,像这样:

    复制代码
    1 class CPoint3d : public CPoint2d {
    2 public:
    3     CPoint3d( float x = 0.0, float y = 0.0, float z = 0.0 ) { _z = z; }
    4 protected:
    5     float _z;
    6 };
    复制代码

      那么并不会出现前面的编译错误。

      以上的讨论是针对base class的建构,同理对于member class 也是一样。如果member calss有一个以上的constructors,那么内含embedded object的那个class就必须在其constructor中指定initialization list,否则一样会出现编译错误。

      initialization list到底会在编译器底层发生什么影响呢?编译器会以“适当的次序”将initialization list中指定的member调用动作安插到constructor之内,并置于任何user code之前,下面这张图可以表现出编译器的插码结果:

     

       有一些微妙的地方必须注意,编译器安插在constructor中的members声明动作是以members在class中的声明次序为根据,而不是以initializtion list中的排序为根据。如果两者在外观上错乱,很容易引起程序设计时的一些困扰或失误。例如:

    复制代码
    class X {
    public:
        X(int val) : m_data2(val), m_data1(m_data2) { }
    protected:
        int m_data1;
        int m_data2;
    };
    复制代码

      我们很容易误以为在X constructor中是以val 设定m_data2,再将m_data2设定给m_data1.但根据两个data members的声明顺序,实际发生的动作却是:

    1 X::X(int val)
    2 {
    3     m_data1(m_data2); // 此时 m_data2 还没有初值,糟糕
    4     m_data2(val);
    5 }

      于是,当我们产生一个X object:

    X x(3);

      其实data members的内容可能成为这样:

    x.m_data1 = -2124198216 // 这不是我们希望的
    x.m_data2 = 3

    一个比较好的做法是,把class X重新设计如下:

    复制代码
    1 class X {
    2 public:
    3     X(int val) : m_data2(val) { m_data1 = m_data2; }
    4 protected:
    5     int m_data1;
    6     int m_data2;
    7 };
    复制代码

     

  • 相关阅读:
    什么是轮式里程计
    c语言字符函数和字符串函数
    NCV1117ST50T3G线性稳压器芯片中文资料规格书PDF数据手册引脚图图片价格参数
    Linux内核源码分析 (B.11) 从内核世界透视 mmap 内存映射的本质(原理篇)
    免费开源的积分商城系统_积分商城的功能逻辑_OctShop
    6毛钱SOT-23封装28V、400mA 开关升压转换器,LCD偏置电源和白光LED应用芯片TPS61040
    java计算机毕业设计风情旅游网站MyBatis+系统+LW文档+源码+调试部署
    看云笔记和有道云笔记哪个好用,看谁更胜一筹
    应用引导页配置相关 - iOS
    Optional用法与争议点
  • 原文地址:https://www.cnblogs.com/ruanchunyi/p/18196886