层次结构中的所有派生类是否都需要访问虚拟基类?

Tim*_*rse 6 c++ constructor virtual-inheritance

当我尝试编译以下代码时:

class A {
public:
    A(int v) : virt(v) { }
    int virt;
    int getVirt(void) const { return virt; }
};

class B : private virtual A {
protected:
    B(int v) : A(v) { }
    using A::getVirt;
};

class C : public B, private virtual A {
protected:
    C(int v) : A(v), B(v) { }
    using A::getVirt;
};

class D : public C {
public:
    D(void) : C(3) { }
    using C::getVirt;
};

#include <iostream>

int main(int argc, char *argv[]) {
    D d;
    std::cout << "The number is: " << d.getVirt() << std::endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我得到一个关于D没有实例化A的错误; 那是对的吗?如果在层次结构中嵌入了虚拟基础,那么所有派生类还需要从虚拟基础派生,因此它们可以调用虚拟基础的参数构造函数吗?

顺便说一句,这是G ++产生的错误:

Main.cpp: In constructor ‘D::D()’:
Main.cpp:22:18: error: no matching function for call to ‘A::A()’
Main.cpp:22:18: note: candidates are:
Main.cpp:3:5: note: A::A(int)
Main.cpp:3:5: note:   candidate expects 1 argument, 0 provided
Main.cpp:1:7: note: A::A(const A&)
Main.cpp:1:7: note:   candidate expects 1 argument, 0 provided
Run Code Online (Sandbox Code Playgroud)

Ker*_* SB 6

这与访问控制无关(至少不是主要).相反,您必须了解虚拟基础的工作原理:虚拟基础子对象由最派生的类初始化.由于您没有A在构造函数初始化列表中提及,因此D尝试了默认构造函数,但不存在.

要解决这个问题,请AD以下方面正确使用:

 D() : A(3), C(3) { }
Run Code Online (Sandbox Code Playgroud)

当你说A(3),名称查找是根据12.6.2/2执行的:

mem-initializer-id中,在构造函数的类的范围内查找初始的非限定标识符,如果在该范围内找不到,则在包含构造函数定义的范围中查找它.

正如Drew Dorman正确指出的那样,您可以通过调用它来强制直接路径到虚拟基类::A,从而获得所需的访问权限.

  • 如果只更改D的构造函数,它将无法编译,我在C++中尝试了更改的代码,它报告:错误:"类A"无法访问.主要是因为在A类和B类之间使用了私有继承.所以我猜它确实与访问控制有关. (3认同)

Dre*_*ann 1

正如 Kerrek SB 提到的,您需要A在 的构造函数中进行初始化D

但是,您还必须显式告诉编译器您A使用作用域运算符从其(私有)派生上下文进行访问。

class D : public C {
public:
    D(void) : ::A(3), C(3) { }
//            ^^ Access this constructor from a global context
    using C::getVirt;
};
Run Code Online (Sandbox Code Playgroud)

这也意味着您的构造函数必须是公共的,就像您的代码一样。

  • +1 非常好。有趣的一点似乎是通过在初始化程序中提及“A”来进行的“名称查找”(参见 12.6.2/2)。构造函数类范围内的名称首先出现,然后才考虑环境范围内的名称。添加范围解析可以获得类的“正确”路径。 (2认同)