我在一个层次结构中有3个类(称为A、B和C),其中B扩展A和C扩展B。C的定义要求调用A的构造函数,所以我试图在B中创建一个构造函数,但是编译器告诉我C的构造函数必须同时初始化A和B,这在我看来是违反直觉的,因为它应该只初始化一次。
下面是更好地说明我所面临的问题的代码:
#include <iostream>
struct A {
A(std::string name) : name_(name) {
std::cout << "A ctor called: " << name << std::endl;
}
std::string name_;
};
struct B : virtual public A {
// This constructor is required or else subclasses cannot be constructed properly
B(std::string name) : A(name) {
std::cout << "B ctor called: " << name << std::endl;
}
};
struct C : virtual public B {
// ERROR: constructor for 'C' must explicitly initialize the base class 'A' which does not have a default constructor
// C() : B("hey") {}
// ERROR: constructor for 'C' must explicitly initialize the base class 'B' which does not have a default constructor
// C() : A("hey") {}
// ok... but have to pass the same name twice & init'ed twice!
C() : A("wat"), B("hey") {
std::cout << "C ctor called" << std::endl;
}
// gcc reorders the constructor invocations...
// here it's written as B then A but it would be init'ed in the order of A then B
// C() : B("hey"), A("wat") {
// std::cout << "C ctor called" << std::endl;
// }
// ok... we can just pass a name but it's still init'ed twice!
// C(std::string name) : B(name), A(name) {}
};
int main() {
C c;
std::cout << c.name_ << std::endl;
}
当我运行密码时,我得到:
A ctor called: wat
B ctor called: hey
C ctor called
wat
我的问题是:
hey
,但是name_
字段包含wat
(前面设置的)?发布于 2021-04-22 11:55:06
在你的解释中,你并没有说你实际上在做这件事。在看了你的代码之后,我走了。首先,扪心自问:“为什么我需要虚拟的派生?”很可能没有很好的理由去做。如果你认为有一个很好的理由,很可能是没有。如果您仍然坚持实际上派生,没有很好的理由,请参见:https://isocpp.org/wiki/faq/multiple-inheritance
发布于 2021-04-22 14:30:10
一个几乎继承的类总是由“大多数派生类”继承的。您的类C
(大部分)完全等同于:
struct C : virtual public A, virtual public B {
对于所有意图和目的,C
继承自A
(当它是派生最多的类时),无论您是否显式地声明它。这就是虚拟继承在C++中的含义。
每个类的构造函数负责构造它继承的所有类。这包括几乎继承的类。它们是否是显式继承的。即使你有:
struct C : virtual public B {
由于类C
仍然是从A
继承的(不管您喜欢与否),它的所有构造函数都必须正式构造A
,除非A
有一个合适的默认构造函数,在这种情况下,当E 215
E 215E 119是最派生的类E 220时,A得到默认构造函数(稍后将详细介绍)。
事情变得更复杂了。假设你尽了自己的职责:
C::C(...) : A{ ... }, B{ ... } // The actual parameters are irrelevant
假设您现在声明了以下内容之一:
C an_instance_of_c{ ... };
您成功地为C (“大多数派生类”)调用了这个构造函数,并且按照您的说明,它顺从地构造了A和B。
现在假设您创建了从D继承的C
struct D : public C { ... }
然后继续构建一个D
D::D(...) : A{ ... }, B{ ... }, C{ ... }
您刚刚发现,D现在负责构建A和B,原因与我刚才解释的相同。当然,它还负责构建C。
现在,假设C的构造函数的参数最终调用了上面所示的构造函数。
嗯,你猜怎么着,即使你写了构造函数来构造A和B,这个构造函数实际上将是它所做的一切,,除了。D现在负责构建A和B,但是即使调用相同的C构造函数,它也会执行而不是做任何与A和B相关的操作。毕竟,A和B已经是由D构建的了。但是C将构建它所构建的其他所有东西。
当您有一个虚拟继承类时,您显式或隐式定义的每个构造函数在C++编译器中自动编译的代码--两个单独的构造函数:一个构造所有虚拟继承的类,另一个不构造。您的C++编译器还将生成代码来调用每个构造函数的适当版本,而这个类要么是正在构造的“大多数派生类”,要么不是。
由于虚拟继承所造成的额外复杂性,以及由此而产生的所有陷阱和问题(例如,即使您私下和实际上继承了一个类,子类也可以公开地从同一个类继承,并且保护和公共访问您认为您私下继承的所有东西),您偶尔会遇到这种情况。当然,所有这些都是正确的,但是如果您完全了解虚拟继承是如何工作的,以及它是如何工作的,那么使用虚拟继承没有什么错,而且它允许在C++中完成任何一个对等程序都无法完成的任务。
https://stackoverflow.com/questions/67219462
复制相似问题