面试集锦--构造函数初始化顺序问题

从 C++ 托管到 Visual C++,类构造函数的初始化顺序已经发生变化。

构造函数初始化顺序的比较

在 C++ 托管扩展中,构造函数的初始化是按以下顺序进行的:

  1. 如果存在基类的构造函数则调用该构造函数。

  2. 计算该类的初始化列表。

  3. 执行类构造函数的代码正文。

此执行顺序与本机 C++ 编程遵守相同的约定。 新 Visual C++ 语言规定了 CLR 类的执行顺序,如下所示:

  1. 计算该类的初始化列表。

  2. 如果存在基类的构造函数则调用该构造函数。

  3. 执行类构造函数的代码正文。

    上面的文字用代码表示就是:

    __gc class A
    {
    public:
       A() : _n(1)
       {
       }
    
    protected:
       int _n;
    };
    
    __gc class B : public A
    {
    public:
       B() : _m(_n)
       {
       }
    private:
       int _m;
    };
     按照上述的构函数初始化顺序,在构造类 B 的新实例,我们应看到如下的执行顺序:
    1.调用基类 A 的构造函数。 将 _n 成员初始化为 1。 
    
    2.计算类 B 的初始化列表。 将 _m 成员初始化为 1。 
    
    3.执行类 B 的代码正文。
     现在请考虑以新 Visual C++ 语法表示的相同代码:
     ref class A
    {
    public:
       A() : _n(1)
       {
       }
    
    protected:
       int _n;
    };
    
    ref class B : A
    {
    public:
       B() : _m(_n)
       {
       }
    private:
       int _m;
    };
     按照新语法构造的类 B 的新实例的执行顺序如下:
    1.计算类 B 的初始化列表。 将 _m 成员初始化为 0(0 是 _m 类成员的未初始化值)。 
    
    2.调用基类 A 的构造函数。 将 _n 成员初始化为 1。 
    
    3.执行类 B 的代码正文。
    
     请注意,对于这些代码示例,相似的语法产生了不同的结果。 类 B 的构造函数依赖于基类 A 的值来初始化其成员。 但并未调用类 A 的构造函数。 如果需要分配内存或资源才能在基类构造函数中生成继承类,则此种依赖关系将是非常危险的。 
    

    另外更为详细的构造顺序如下:

    C++构造函数按下列顺序被调用:
    (1)任何虚拟基类的构造函数按照它们被继承的顺序构造;
    (2)任何非虚拟基类的构造函数按照它们被继承的顺序构造;
    (3)任何成员对象的构造函数按照它们声明的顺序调用;
    (4)类自己的构造函数。

    #include <iostream>
    
    using namespace std;
    
    class OBJ1
    {
    public:
        OBJ1(){ cout <<"OBJ1\n"; }
    };
    
    class OBJ2
    {
    public:
        OBJ2(){ cout <<"OBJ2\n"; }
    };
    
    class Base1
    {
    public:
        Base1(){ cout <<"Base1\n"; }
    };
    
    class Base2
    {
    public:
        Base2(){ cout <<"Base2\n"; }
    };
    
    class Base3
    {
    public:
        Base3(){ cout <<"Base3\n"; }
    };
    
    class Base4
    {
    public:
        Base4(){ cout <<"Base4\n"; }
    };
    
    class Derived :public Base1, virtual public Base2,
        public Base3, virtual public Base4
    {
    public:
        Derived() :Base4(), Base3(), Base2(),
            Base1(), obj2(), obj1()
        {
            cout <<"Derived ok.\n";
        }
    protected:
        OBJ1 obj1;
        OBJ2 obj2;
    };
    
    int main()
    {
        Derived aa;
        cout <<"This is ok.\n";
    
        int i;
        cin >> i;
    
        return 0;
    }
    结果:
    Base2
    Base4
    Base1
    Base3
    OBJ1
    OBJ2
    Derived ok.
    This is ok.



    分析以上输出结果可以得出以下结论:在被继承的基类中,虚基类优先级最高,其次是普通的基类,然后是本类的成员函数按照顺序构造。在所有的虚基类中按照被继承的顺序对各个类中成员进行初始化。同样在所有的普通基类中按照被继承的顺序对各个类中成员进行初始化。
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值