【问题标题】:Defining a virtual method inherited more than once定义多次继承的虚方法
【发布时间】:2018-04-30 09:23:51
【问题描述】:

我一直试图找到这个问题的答案,但我找不到(我什至不知道如何正确地表述这个问题)所以我决定在 StackOverflow = 上写我的第一篇文章。

上下文如下:

我有这个父类:

class Parent
{
public:
    Parent(){};
    void foo(void)
    {
        //Do some common things
        bar();
        //Do some more common things
    };
protected:
    virtual void bar(void) = 0;
};

我想创建无限数量的派生子:

class Child1 : public Parent
{
public:
    Child1() : Parent(), child1Variable(0) {};
protected:
    virtual void bar(void) = 0;
private:
    uint32_t child1Variable;
};

class Child2 : public Parent
{
public:
    Child2() : Parent(), child2Variable(0) {};
protected:
    virtual void bar(void) = 0;
private:
    uint32_t child2Variable;
};

.
.
.

class ChildN : public Parent
{
public:
    ChildN() : Parent(), childNVariable(0) {};
protected:
    virtual void bar(void) = 0;
private:
    uint32_t childNVariable;
};

原因主要是没有重复Parent的foo()中的代码

然后我想创建我的最终实例化类,例如:

class ExampleFinal : public Child1, public Child3, public Child27
{
    //How to define Child1::bar(), Child3::bar() and Child27::bar() ??
private:
    void bar(void); //????
};

所以问题是:

  1. 如何定义(滥用符号)ExampleFinal::Child1::barExampleFinal::Child3::bar、...的方法
  2. 我是否如此坚持这一点而忽略了一个更简单的解决方案?

最终目标是能够做到以下几点:

ExampleFinal test;
test.Child1::foo(); //should end up on "ExampleFinal::Child1::bar"
test.Child3::foo(); //should end up on "ExampleFinal::Child3::bar"

谢谢!

【问题讨论】:

  • 你知道你不需要在派生类中重复virtual void bar(void) = 0;吗?
  • 如果您需要无限数量的子实例,那么您不需要无限数量的子类定义,只需一个。
  • 为什么需要不同的Child 类?你不能只有Children 的向量吗?
  • 那么......每个孩子真的需要他们自己的成员覆盖吗?我的意思是,无论如何,您似乎只关心最派生的类。
  • 我很确定这将是继承滥用;我无法接受 ExampleFinal“是”Child1Child2 和 ....

标签: c++


【解决方案1】:

实现ExampleFinal::bar()(旁注:bar(void) 是一个在 C++ 中没有用的 C 主义)将覆盖您一次声明的所有 bars。如果你想有不同的版本,你需要插入另一层类:

struct GrandChild1 : Child1 {
    void bar() override { /*...*/ }
};

// And so on...

struct ExampleFinal : GrandChild1, GrandChild3, GrandChild27 {
    // Nothing needed here.
};

那么您描述的行为将起作用。但是请注意,您的继承图意味着ExampleFinal 有一个Parent 子对象每个Child。这本身不是问题,但可能无法模拟您想要的 - 也许您需要虚拟继承,但要小心兔子洞。


如果您想将所有ChildN::bars 的覆盖保留在ExampleFinal 中,您可以添加标签调度来识别它们,但需要多进行一次虚拟调用:

struct Parent {
    void foo() {
        bar();
    };

protected:
    template <class Child>
    struct tag { };
    virtual void bar() = 0;
};

struct Child1 : Parent {
protected:
    virtual void bar(tag<Child1>) = 0;

    void bar() final override {
        return bar(tag<Child1>{});
    }

    int child1Var;
};

struct Child2 : Parent {
protected:
    virtual void bar(tag<Child2>) = 0;

    void bar() final override {
        return bar(tag<Child2>{});
    }

    int child2Var;
};

struct ExampleFinal : Child1, Child2 {
protected:
    using Parent::tag;

    void bar(tag<Child1>) final override {
        std::cout << "Child1::bar\n";
    }

    void bar(tag<Child2>) final override {
        std::cout << "Child2::bar\n";
    }
};

请注意,bar()bar(tag&lt;ChildN&gt;) 的桥很容易隐藏在宏后面。如果你想避免第二次虚拟通话的费用,这里也可以申请 CRTP。

【讨论】:

  • 感谢您的回答,我认为添加更多层是可行的,但它开始变得混乱。我知道最终对象将包含每个孩子的Parent 对象,但我认为这是能够覆盖每个孩子的 bar 所必需的。我的计划是让Parent 虚拟地从一个包含几个公共变量的基类继承给每个孩子。
  • @Kai 我在我的解决方案中添加了另一个旋转。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-09
  • 1970-01-01
  • 1970-01-01
  • 2013-11-09
  • 2011-08-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多