【问题标题】:Why is constructor of a grand parent deleted in this case?为什么在这种情况下删除祖父母的构造函数?
【发布时间】:2026-01-27 15:50:01
【问题描述】:

编译器抱怨 D 的构造函数被删除,因为格式不正确,为什么?

#include<iostream>
using namespace std;

class A
{
   int x;
   public:
   A(int i) { x = i; }
   void print() { cout << x; }
};

class B: virtual public A
{
   public:
      B():A(10) { }
};

class C: virtual public A 
{
   public:
      C():A(10) { }
};

class D: public B, public C {
};

int main()
{
   D d;
   d.print();
   return 0;
}

输出

main.cpp:37:4: 错误:使用已删除的函数 'D::D()' D d; ^ main.cpp:32:7: 注意:'D::D()' 被隐式删除,因为默认定义格式错误:class D: public B, public C { ^

【问题讨论】:

  • 因为没有默认构造函数可供D 初始化这些基类。
  • 因为A没有默认构造函数。
  • 因为虚拟继承D必须直接调用A的ctor并且没有默认的那个
  • 不是直接欺骗但可能会有所帮助*.com/questions/17184514/…

标签: c++ multiple-inheritance


【解决方案1】:

由于虚拟基类的初始化规则,

class D: public B, public C {
};

相当于:

class D: public B, public C {
    public:
       D() : A(), B(), C() {}
};

这就是为什么你不能在 D 的实例中创建。

解决方案 1

更改A,使其具有默认构造函数。

class A
{
     int x;
   public:
     A(int i = 0) { x = i; }
     void print() { cout << x; }
};

解决方案 2

D 更改为:

class D: public B, public C {
    public:
       D() : A(0), B(), C() {}
};

或更简单的版本,

class D: public B, public C {
    public:
       D() : A(0) {}
};

【讨论】:

  • 我认为没有必要显式调用BC 的默认ctors
【解决方案2】:

这是因为D 间接使用virtual 继承自A。 A 没有无参数构造函数,因此无法为 D 创建编译器生成的构造函数。

【讨论】:

    【解决方案3】:

    注意:这主要是添加对标准的引用,以防万一有人关心(但像往常一样,@R. Sahu 的回答非常准确)。

    标准规定 ([class.base.init]/13):

    在非委托构造函数中,初始化在 以下顺序:
    (13.1) — 首先,并且仅适用于 大多数派生类(6.6.2),虚拟基类在 它们出现在深度优先从左到右遍历 基类的有向无环图,其中“从左到右”是 派生类中基类的出现顺序 base-specifier-list.
    (13.2) — 那么,直接基类是 按照声明顺序初始化,因为它们出现在 base-specifier-list(不考虑 mem-initializers 的顺序)。

    所以,由于A 是一个虚拟基类,它直接由最派生类(D)初始化。只有在之后,直接基类才会被初始化——但要编译任何东西,最派生类必须能够初始化虚拟基类。

    在这样的情况下,有些人可能会觉得有趣。让我们稍微修改一下您的类结构,因此我们进行必要的初始化,并且(重要的是)在每个构造函数中使用唯一值进行初始化:

    #include <iostream>
    
    class A {
        int i;
     public:
        A(int i) : i(i) {}
    
        void show() { std::cout << "value: " << i << "\n"; }
    };
    
    class B : virtual public A{
    public:
        B() : A(10) {}
    };
    
    class C : virtual public A { 
    public:
        C() : A(20) {}
    };
    
    class D : public B, public C {
    public:
        D() : A(0) {}
    };
    
    int main() {
        D d;
        d.show();
    }
    

    在这种情况下,究竟会发生什么?我们有三个不同的构造函数,每个都“认为”它将用不同的值初始化A 对象?哪个“赢”了?

    答案是派生最多的构造函数 (D::D) 中的那个是用来初始化虚拟基类对象的那个,所以它是“获胜”的那个。当我们运行上面的代码时,它应该打印出0

    【讨论】:

      最近更新 更多