【问题标题】:Order of constructor call in virtual inheritance虚拟继承中的构造函数调用顺序
【发布时间】:2012-05-10 12:40:56
【问题描述】:
class A {
        int i;
public: 
        A() {cout<<"in A's def const\n";};
        A(int k) {cout<<"In A const\n";  i = k; }
        };

class B : virtual public A {
public:
        B(){cout<<"in B's def const\n";};
        B(int i) : A(i) {cout<<"in B const\n";}
        };

class C :   public B {
public:
        C() {cout<<"in C def cstr\n";}
        C(int i) : B(i) {cout<<"in C const\n";}
        };

int main()
{
        C c(2);
        return 0;
}

这种情况下的输出是

in A's def const
in B const
in C const

为什么这不进入in A const

`它应该遵循 1 arg 构造函数调用的顺序。 但是使用 virtual 关键字从 A 派生 B 实际发生了什么。

还有几个问题

即使我删除了上述程序中的 virtual 关键字并删除了所有默认构造函数,它也会出错。那么,为什么它需要 def 构造函数

【问题讨论】:

    标签: c++ constructor virtual-inheritance


    【解决方案1】:

    虚拟基类的构造函数总是从最派生类调用,使用它可能传入的任何参数。在你的情况下,最派生类没有为A指定初始化器,所以默认构造函数是用过。

    【讨论】:

    • @james....你的意思是说这里最派生的类,即 C 应该为 A 指定初始化程序。但是,在删除 A 默认构造函数并添加 C(int i) 时:A( i),B(i) 在 C 的构造函数中不起作用
    • @MikeDeSimone...是的,我写了同样的内容,但删除了 A 的默认构造函数,但我得到编译错误...你能解释一下为什么会这样..如果我有如何照顾涉及复杂的继承。
    • 删除 A 的默认构造函数后的错误是什么? B 和 C 的默认构造函数现在必须通过显式调用 A 构造函数来扩展,这不是问题吗?
    • 只要B(或C)有一个没有为A指定初始化器的构造函数,A必须有一个默认构造函数。您从未真正调用过构造函数这一事实并不意味着编译器不会为它生成代码,并且生成的代码将尝试调用A()(有条件地,测试它是否是最衍生的类)。
    • @MikeDeSimone A(int) 构造函数将被调用,如果最派生类在初始化列表中指定它:例如C::C(int i) : A(i), B(i) {} 虚基的初始化总是由最派生的类来完成。
    【解决方案2】:

    作为JamesKanze has explained,在virtual 继承的情况下,它是调用虚拟基类的构造函数的最派生类。因此,如果您希望 A 的构造函数需要调用一个整数,则需要将其添加到 C 的初始化列表中。

    C(int i) : A(i), B(i) {cout<<"in C const\n";}
    

    对于您问题的第二部分,不需要默认构造函数,但是派生类必须显式调用非默认构造函数,因为在没有非默认构造函数的情况下编译器无法为您执行此操作.

    #include <iostream>
    using namespace std;
    
    class A {
      int i;
    public:
      // A() {cout<<"in A's def const\n";};
      A(int k) {cout<<"In A const\n";  i = k; }
    };
    
    class B : virtual public A {
    public:
      // B(){cout<<"in B's def const\n";};
      B(int i) : A(i) {cout<<"in B const\n";}
    };
    
    class C :   public B {
    public:
      C() : A(42), B(42) {cout<<"in C def cstr\n";}
      C(int i) : A(i), B(i) {cout<<"in C const\n";}
    };
    
    int main()
    {
      C c(2), c2;
      return 0;
    }
    

    打印出来

    In A const
    in B const
    in C const
    In A const
    in B const
    in C def cstr
    

    【讨论】:

    • @praetorian...感谢您的回答。但是我仍然不清楚一件事....为什么有必要给 C() : A(42), B(42) ?请澄清
    • @Kunal 每个派生类构造函数(对于非 POD 类型)都必须初始化其基类构造函数。当您没有显式列出基本构造函数时,编译器将为您调用默认构造函数。如果不存在这样的默认基础构造函数,则必须显式调用现有的基础构造函数。
    • 这是否意味着 A() 将被调用两次?在 B() 中调用 A() 会发生什么?
    • @Youda008 不,A() 不会被调用两次。在示例输出语句中,前 3 行在c 的构造期间打印,接下来的 3 行在c2 的构造期间打印。只有在构造B 时,才会执行在B 的内存初始化器列表中对A 的构造函数的调用。如果你构造了一个C,那么那个类负责构造A
    【解决方案3】:

    这里有两个问题。

    为什么这不进入A const?

    因为您使用的是虚拟继承。

    当您使用虚拟继承时,Initialization list of most-derived-class's ctor directly invokes the virtual base class's ctor..在这种情况下,这意味着C 的构造函数直接调用A 的构造函数。由于在C的初始化列表中没有指定调用哪个A构造函数,所以调用的是默认构造函数。

    通过将C::C(int) 的实现更改为:

    C(int i) : A(i), B(i) {cout<<"in C const\n";}
    

    如果我删除上述程序中的 virtual 关键字并删除所有 默认构造函数它给出错误。那么,为什么它需要 def 构造函数?

    因为B 也没有指定调用哪个A ctor,所以使用了默认构造函数。如果去掉As def ctor,B就不能编译了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-10-11
      • 2015-06-21
      • 2013-01-29
      • 2011-11-24
      • 1970-01-01
      相关资源
      最近更新 更多