简体   繁体   English

如何创建仅调用祖父构造函数的构造函数?

[英]How to create constructor that calls to grand parent constructor only?

I have 3 classes in a hierarchy (call it A, B, and C) where B extends A and C extends B. Class A has a constructor that takes a single argument.我在一个层次结构中有 3 个类(称为 A、B 和 C),其中 B 扩展 A,C 扩展 B。Class A 有一个带有单个参数的构造函数。 The definition of C requires that A's constructor to be called, so I attempted to do that by creating a constructor in B. However, the compiler is telling me that C's constructor must initialize both A and B. That seems counter-intuitive to me because it should really be initialized just once. C 的定义要求调用 A 的构造函数,所以我试图通过在 B 中创建构造函数来做到这一点。但是,编译器告诉我 C 的构造函数必须同时初始化 A 和 B。这对我来说似乎违反直觉,因为它真的应该只初始化一次。

Here's the code to better illustrate the issue that I'm facing:这是更好地说明我面临的问题的代码:

#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;
}

When I run the code, I got:当我运行代码时,我得到:

A ctor called: wat
B ctor called: hey
C ctor called
wat

My questions are:我的问题是:

  1. Is there a cleaner way to write it such that I don't have to call into both A & B's constructor explicitly?有没有更简洁的方法来编写它,这样我就不必显式地调用 A & B 的构造函数?

  2. Why does the output shows hey being set at a later time but the name_ field contains wat (which was set earlier)?为什么 output 显示hey是稍后设置的,但name_字段包含wat (这是之前设置的)?

In your explanation you didn't say you are doing this virtually.在您的解释中,您并没有说您实际上是在这样做。 After seeing your code, I went OMG.看到你的代码后,我去了 OMG。 First of all, ask yourself “why do I need to derive virtually?”首先,问问自己“为什么我需要虚拟推导?” The chances are there is no good reason for it.很可能没有充分的理由。 If you think there is a good reason for it, the chances are there is not.如果您认为有充分的理由,那么很有可能没有。 If you still insist on deriving virtually, with no good reason, see: https://isocpp.org/wiki/faq/multiple-inheritance如果您仍然坚持虚拟派生,没有充分的理由,请参阅: https://isocpp.org/wiki/faq/multiple-inheritance

A virtually-inherited class is always inherited by the "most derived class".虚拟继承的 class 始终由“最派生类”继承。 Your class C is (mostly) exactly equivalent to:您的 class C (大部分)完全等同于:

struct C : virtual public A, virtual public B {

For all intents and purposes C inherits from A , when it is the most-derived class, whether you explicitly declare it, as such, or not.出于所有意图和目的, C继承自A ,当它是最衍生的 class 时,无论您是否明确声明它。 That's what virtual inheritance means, in C++.这就是虚拟 inheritance 在 C++ 中的含义。

Every class's constructor is responsible for constructing all classes it inherits from.每个类的构造函数负责构造它所继承的所有类。 This includes virtually-inherited classes.这包括虚拟继承的类。 Whether they are explicitly inherited, or not.它们是否是显式继承的。 Even though you have:即使你有:

struct C : virtual public B {

Since class C is still inheriting from A (whether you like or not), all of its constructors must formally construct A , unless A has a suitable default constructor, in which case A gets default constructed when C is the most-derived class (more on that later). Since class C is still inheriting from A (whether you like or not), all of its constructors must formally construct A , unless A has a suitable default constructor, in which case A gets default constructed when C is the most-derived class (more稍后再说)。

Here's where this gets even more complicated.这就是变得更加复杂的地方。 Let's say you do your job:假设你做你的工作:

C::C(...) : A{ ... }, B{ ... } // The actual parameters are irrelevant

Let's say you now declare one of these things:假设您现在声明以下内容之一:

C an_instance_of_c{ ... };

You successfully invoked this constructor for C , the "most derived class", and it obediently constructed A and B , as per your instructions.您成功地为“最派生类” C调用了此构造函数,并且按照您的指示,它乖乖地构造AB

Now let's say that you created D that inherits from C :现在假设您创建了继承自CD

struct D : public C { ... }

Then you go ahead and construct a D :然后你 go 提前并构造一个D

D::D(...) : A{ ... }, B{ ... }, C{ ... }

You've just figured out that D is now responsible for constructing A and B , for the same exact reasons that I just explained.您刚刚发现D现在负责构建AB ,原因与我刚刚解释的完全相同。 It's also responsible for constructing C , of course.当然,它还负责构建C

Now let's say that the parameters to C 's constructor here end up invoking the same constructor that appears above.现在假设C的构造函数的参数最终调用了上面出现的相同构造函数。

Well, guess what, even though you wrote that constructor to construct A , and B , that constructor will actually everything else it does, except that .好吧,猜猜看,即使您编写了构造函数来构造AB ,该构造函数实际上将执行它所做的所有其他事情,除了. D is now responsible for constructing both A and B , but even though the same C constructor gets called, it does not do anything that's related to A and B . D现在负责构造AB ,但是即使调用了相同的C构造函数,它也不会执行与AB相关的任何事情。 After all, A and B are already constructed by D .毕竟, AB已经D构造。 But C will construct everything else it otherwise constructs.但是C将构建它以其他方式构建的所有其他内容。

When you have a virtually-inherited class, every constructor you explicitly or implicitly define results in your C++ compiler automatically compiling code for two separate constructors : one that constructs all virtually-inherited classes, and one that does not.当您有一个虚拟继承的 class 时,您显式或隐式定义的每个构造函数都会导致 C++ 编译器自动为两个单独的构造函数编译代码:一个构造所有虚拟继承的类,一个不构造。 Your C++ compiler will also end up generating code to invoke the appropriate version of each constructor, when this class is either the "most derived class" that's getting constructed, or not.您的 C++ 编译器也将最终生成代码以调用每个构造函数的适当版本,当此 class 是正在构造的“最派生类”时,或者不是。

You will occasionally encounter virtual inheritance getting bad-mouthed, due to all the extra complexity that it creates, and all the pitfalls and gotchas that result from that (such as even if you privately and virtually inherit from a class, a subclass can always publicly virtually inherit from the same class and have protected and public access to everything you think you privately inherited).您偶尔会遇到虚拟 inheritance 被人说坏话,因为它会产生所有额外的复杂性,以及由此产生的所有陷阱和陷阱(例如,即使您私下和虚拟地从 class 继承,子类也总是可以公开几乎从同一个 class 继承,并且对您认为您私人继承的所有内容具有保护和公共访问权限)。 All of that is true, of course, but if you fully understand how virtual inheritance works, and what it does, there's nothing wrong with using it, and it allows things to be done in C++ that cannot be done in any of its peers.当然,所有这些都是正确的,但是如果您完全了解虚拟 inheritance 的工作原理以及它的作用,那么使用它没有任何问题,并且它允许在 C++ 中完成在任何同行中都无法完成的事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM