C++ 多重继承:使用 base class A 实现 base class B 的抽象方法

C++ Multiple Inheritance: Using base class A's implementation for abstract method of base class B

我有一个抽象 class Interface 声明了 f()g() 方法。

MyClass 实现了 Interface::g(),而 MyClass 继承自 Base,它实现了自己的 f().

如何让 Base::f() 成为 MyClassInterface::f() 的实现?

如果可能的话,我不想让 Base 直接继承自 Interface

class Interface {
    public:
        virtual void f() = 0;
        virtual void g() = 0;
};

class Base {
    public:
        void f() { }
};

class MyClass : public Interface, public Base {
    public:
        void g() { }
};

int main() {
    MyClass c;
    c.f();
    c.g();
}

这不行,因为fMyClass中仍然是一个纯虚函数。

在Java中,我会使用下面的方法,但它显然不适用于C++。

public class MyClass extends Base implements Interface {
    public static void main(String argv[]) {
        MyClass c = new MyClass();
        c.f();
        c.g();
    }
    public void g() { }
}

class Base {
    public void f() { }
}

interface Interface {
    public void f();
    public void g();
}

我要解决的具体问题如下:

上面例子中的 Interface 是一个抽象 class Sender 具有与发送 MIDI 消息相关的不同方法,例如update()(检查是否应该发送消息,必要时发送),setAddress() 更改消息地址。消息是 MIDI 事件。我必须更改 address/channel 以实现移调和选择不同库等功能。

Baseclass是classButton。它有一个方法 update() 检查按钮是否被按下,并相应地调用(虚拟)方法 press()release()

MyClass 然后会实现 press()release() 方法,以便在按钮状态改变时发送正确的数据。

还有其他类型的 Sender,我将它们全部保存在一个列表中以一次性 update() 它们,而不管 Sender 的具体类型。我还需要能够更改列表中所有 Sender 的地址(一次全部)。

setAddress()方法与按钮无关,所以Button继承自Sender没有意义。
还有其他 classes 可以实现 Button,但它们并不都是 Senders。

也许我必须完全改变结构,但目前我真的没有看到更好的解决方案。

class Button {
    public:
        void update() {
            if ( /* check in hardware whether button is pressed */ )
                press();
        }
        virtual void press() = 0;
};

class Sender {
    public:
        virtual void update() = 0;
        virtual void setAddress(int address) = 0;
};

class ButtonSender : public Button, public Sender {
    public:
        void update() {
            Button::update();
        }
        void press() {
            /* send a message */
        }
        void setAddress(int address) {
            /* change the address */
        }
};

int main() {
    ButtonSender bs;
    Sender *s = &bs;
    s->update();
}

class Base 正在 Interface 中实现功能,因此它应该派生自 class。那么MyClass只继承自Base:

class Base : public Interface {
    public:
        void f() {
            cout << "Base::f()" << endl;
        }
};

class MyClass : public Base {
    public:
        void g() {
            cout << "MyClass::g()" << endl;
        }
};

要完成这项工作,您必须覆盖 MyClass 中的 f():

class MyClass : public Interface, public Base {
    public:
        void f() {
            Base::f();
        }
        void g() {
            cout << "MyClass::g()" << endl;
        }
};

为什么你在 InterfaceBase 中都有一个名为 f 的函数,而你随后在 MyClass 中从这两个函数派生?你在那里制造了一个名字冲突,因此你的困难。好的,所以这次您可以使用三行方法来摆脱它。但是下次呢?下一个?不,不要去那里。

C++ 不是 Java - 它 没有 接口。相反,从抽象基础 class 派生的具体 class(或 classes 链 - 使用单一继承)需要实现 all如您所见,class 声明的纯虚方法。

你在这里试图做的事情违背了语言的精神,很可能会给你带来麻烦,所以你需要调整你的想法。

多重继承,尤其是在一个或多个基 classes 中声明相同的方法或数据成员名称时,通常是一种糟糕的设计选择,并且更多时候是症状而不是设计中固有的错误首先是 class 层次结构,所以我的建议是重新审视它 - 你会很高兴你这样做了。

无论如何,现在你知道为什么你 被否决了(尽管不是我)。我希望你觉得这很有用——它是用心良苦的。我不必写这个,但又一次,我想,我在 SO 上经常看到这种事情,我觉得很痛苦。

感谢 OP 发布更具体的示例。它促使我采取不寻常的步骤发布第二个答案。我希望你觉得它有用。

我在这里要做的是向您展示另一种 class 层次结构,我希望您会看到它更适合您要解决的问题。我将重点关注 class Button(这是根本问题所在)与我将称之为 ButtonHandler.[=53= 的东西之间的关系]

现在 ButtonHandler 可以是任何东西 - Button 当然不关心它是什么 - 它只需要在 它拥有的按钮 [=86] 时得到通知=] 被按下或释放。一旦您意识到这是问题的核心,解决方案就会变得更加明显。当然,您可以将此方法应用于 Sender class(只需从 ButtonHandler 派生并实现 OnPressOnRelease),但我会留给你吧。

最后,作为锦上添花的一点,我让我的 Button 将自己添加到您正在谈论的列表中或从中删除,这样他们就可以轻松地以某种方式进行投票循环。

好的,我们走吧。让我们从我们的 ButtonHandler 开始(因为这需要在 class Button 之前声明),这很简单:

#include <set>
#include <iostream>

class Button;

// Our abstract button handler - derive concrete handlers from this
class ButtonHandler
{
public:
    // Constructor
    ButtonHandler ();

    // Destructor (must be virtual!)
    virtual ~ButtonHandler ();

    virtual void OnPress () = 0;
    virtual void OnRelease () = 0;

private:
    Button *m_button;
};

好的,那里没什么可看的。请注意纯虚方法 OnPressOnRelease - 用于从这个派生的具体 classes 重写 - 以及 class Button 的前向声明.在他们看到 class Button 的完整声明之后,我们需要稍后实现构造函数和析构函数主体,因为他们需要调用它的方法。

现在 class Button 本身,以及关联的 ButtonList。请再次注意,因为它只处理 指针 Button,所以 ButtonList 不需要查看 class 的完整声明 Button,这在这里很方便:

std::set<Button *> ButtonList;

// class Button
class Button
{
public:
    // Constructor
    Button (ButtonHandler *button_handler)
    {
        m_button_handler = button_handler;
        ButtonList.insert (this);
    }

    // Destructor
    ~Button () { ButtonList.erase (this); }

    // Poll the button state
    void Poll ()
    {
        // This would normally check (in hardware) whether button is pressed, but here we just fake it
        m_button_handler->OnPress ();
        m_button_handler->OnRelease ();
    }

private:
    ButtonHandler *m_button_handler;
};

// Poll all the buttons that exist
void PollAllButtons ()
{
    for (auto button : ButtonList) { button->Poll (); }
}

那么这是怎么回事?恩:

  • Button 构造函数传递一个指向 ButtonHander 的指针,以便它可以在适当的时候调用其 OnPressOnRelease 方法。
  • STL 的魔力(如果可用!)使 ButtonList 的实现变得微不足道。它只是使用一个 std::set,记录在案 here(请参阅该页面和链接页面上的示例)。按钮在列表中来来去去时添加和删除自己。
  • PollAllButtons 遍历存储在 ButtonList 中的所有 Button * 并为每个调用我选择调用的 Poll ,后者又调用关联的 ButtonHandlers OnPressOnRelease 适当的方法。这使用了一种叫做 "ranged for loop" 的东西,据我所知,它适用于所有 STL 容器 classes.

现在执行ButtonHandler构造函数和析构函数。这些只是创建和删除关联的按钮(因此,Buttonhandler 拥有 按钮):

// ButtonHandler constructor - implementation
ButtonHandler::ButtonHandler ()
{
    m_button = new Button (this);
}

// Buttonhandler destructor - implementation
ButtonHandler::~ButtonHandler () { delete m_button; }

最后,这里包含一个具体的按钮处理程序和最小的测试程序,以向您展示所有这些东西确实有效。您会注意到(这非常好)这与我之前发布的代码(just that bit too clever)没有变化:

// An example concrete button handler
class MyButtonHandler : public ButtonHandler
{
public:
    // Constructor
    MyButtonHandler (int handler_id) { m_handler_id = handler_id; }

    void OnPress () override { std::cout << "MyButtonHandler::OnPress (" << m_handler_id << ")" << std::endl; }
    void OnRelease () override { std::cout << "MyButtonHandler::OnRelease (" << m_handler_id << ")" << std::endl; }
    // ...

private:
    int m_handler_id;
};

// Test program
int main ()
{
    MyButtonHandler bh1 (1);
    MyButtonHandler bh2 (2);
    PollAllButtons ();
}

输出:

MyButtonHandler::OnPress (1)
MyButtonHandler::OnRelease (1)
MyButtonHandler::OnPress (2)
MyButtonHandler::OnRelease (2)

运行 它位于 Wandbox

好了。与你的截然不同(因为你走错了路,没有回头路)。我建议你带上它 运行 带上它,祝你好运。