如何解决如何创建仅调用祖父构造函数的构造函数?
我在层次结构中有 3 个类(称为 A、B 和 C),其中 B 扩展 A 和 C 扩展 B。类 A 有一个接受单个参数的构造函数。 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
我的问题是:
解决方法
在您的解释中,您并没有说您实际上是在这样做。看到你的代码后,我去了OMG。 首先,问问自己“为什么我需要虚拟推导?”很可能没有充分的理由。 如果您认为有充分的理由,那么很可能没有。 如果您仍然坚持虚拟推导,没有充分的理由,请参阅:https://isocpp.org/wiki/faq/multiple-inheritance
,虚拟继承的类总是由“最派生的类”继承。你的类 C
是(大部分)完全等同于:
struct C : virtual public A,virtual public B {
出于所有意图和目的,C
继承自 A
,当它是最派生的类时,无论您是否显式声明它。这就是 C++ 中虚拟继承的含义。
每个类的构造函数负责构造它继承的所有类。这包括虚拟继承的类。它们是否被显式继承。即使你有:
struct C : virtual public B {
由于类 C
仍然继承自 A
(无论您喜欢与否),它的所有构造函数必须正式构造A
,除非A
有合适的默认构造函数,在这种情况下A
获得默认构造当{{ 1}} 是派生最多的类(稍后会详细介绍)。
这就是变得更加复杂的地方。假设你完成你的工作:
C
假设您现在声明其中一项:
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++ 中完成在任何同行中无法完成的事情。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。