C++ 多重继承:使用基类 A 的实现实现基类 B 的抽象方法
C++ Multiple Inheritance: Using base class A's implementation for abstract method of base class B
我有一个抽象类Interface
,它声明了f()
和g()
方法。
MyClass
实现了Interface::g()
,MyClass
继承了Base
,后者实现了自己的f()
。
如何使Base::f()
成为MyClass
中Interface::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();
}
这不起作用,因为f
在MyClass
中仍然是一个纯虚拟函数。
在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
是抽象类Sender
,具有与发送MIDI消息相关的不同方法,例如update()
(检查是否应发送消息,并在必要时发送)和setAddress()
以更改消息的地址。这些消息是MIDI事件。我必须更改地址/通道的功能,如转换和选择不同的银行。
Base
类是Button
类。它有一个方法update()
,用于检查按钮是否被按下,并相应地调用(虚拟)方法press()
和release()
。
然后MyClass
将实现press()
和release()
方法,以便在按钮状态改变时发送正确的数据。
还有其他类型的Sender
,我将它们全部保存在update()
的列表中,不管Sender
的具体类型如何。我还需要能够更改列表中所有Sender
的地址(一次更改所有地址)。
setAddress()
方法与按钮无关,因此从Sender
继承Button
是没有意义的
还有其他类可以实现Button
,但它们并不都是Sender
。
也许我必须彻底改变结构,但目前我真的看不到更好的解决方案。
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();
}
要实现此功能,必须覆盖MyClass
:中的f()
class MyClass : public Interface, public Base {
public:
void f() {
Base::f();
}
void g() {
cout << "MyClass::g()" << endl;
}
};
为什么在Interface
和Base
中都有一个名为f
的函数,而随后在MyClass
中从这两个函数派生?你在那里制造了一场名字冲突,因此你遇到了困难。好的,所以这次你可以用三线法逃脱惩罚。但下次呢?下一个呢?不,不要去那里。
C++不是Java,它没有的接口。相反,正如您所发现的,从抽象基类派生的具体类(或类链-使用单一继承)需要实现该类声明的所有纯虚拟方法。
你在这里试图做的事情违背了语言的精神,很可能会给你带来麻烦,所以你需要调整你的思维。
多重继承,尤其是当在一个或多个基类中声明相同的方法或数据成员名称时,通常是一个糟糕的设计选择,而且更常见的症状是类层次结构设计中固有的错误,所以我的建议是重新审视这一点-你会很高兴你做到了。
无论如何,现在你知道为什么你被否决了(尽管不是我)。我希望你觉得这很有用——这是出于好意。我不必写这篇文章,但话说回来,我想写,我在SO上经常看到这种事情,我觉得很痛苦。
感谢OP发布了一个更具体的例子。这促使我采取了不同寻常的步骤,发布了第二个答案。我希望你觉得它有用。
我在这里要做的是向您展示一个替代类层次结构,我希望您能看到它更适合您想要解决的问题。我将重点讨论类Button
(这是根本问题所在)和我将称之为ButtonHandler
的东西之间的关系。
现在,ButtonHandler
可以是任何东西——Button
当然不在乎它是什么——它只需要在按下或释放它拥有的按钮时得到通知。一旦你意识到这是问题的核心,解决方案就会变得更加明显。当然,您可以将这种方法应用于Sender
类(只需从ButtonHandler
派生它并实现OnPress
和OnRelease
),但我将把这一点留给您。
最后,锦上添花的是,我让我的Button
在你所说的列表中添加和删除它们,这样它们就可以很容易地在某种循环中进行轮询。
好的,我们走吧。让我们从ButtonHandler
开始(因为这需要在类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;
};
好吧,没什么可看的。请注意纯虚拟方法OnPress
和OnRelease
,它们用于覆盖从该方法派生的具体类,以及类Button
的前向声明。我们稍后需要在构造函数和析构函数体看到类Button
的完整声明之后实现它们,因为它们需要调用上面的方法
现在针对类Button
本身以及相关联的ButtonList
。再次注意,因为它只处理指向Button
s的指针,所以ButtonList
不需要看到类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
的指针,以便它可以在适当的时候调用其OnPress
和OnRelease
方法- STL的魔力(如果可用!)使
ButtonList
的实现变得微不足道。它只是使用std::set
,此处对此进行了说明(请参阅该页面和链接页面上的示例)。按钮来来去去时会将自己添加到该列表中或从该列表中删除 PollAllButtons
遍历存储在ButtonList
中的所有Button *
,并为每一个调用我选择的调用Poll
的内容,这反过来又适当地调用相关的ButtonHandler
的OnPress
和OnRelease
方法。这使用了一种叫做"ranged for loop"的东西,它可以与AFAIK一起使用所有STL容器类
现在是ButtonHandler
构造函数和析构函数的实现。这些只是创建和删除相关的按钮(因此,Buttonhandler
拥有按钮):
// ButtonHandler constructor - implementation
ButtonHandler::ButtonHandler ()
{
m_button = new Button (this);
}
// Buttonhandler destructor - implementation
ButtonHandler::~ButtonHandler () { delete m_button; }
最后,这里包含了一个具体的按钮处理程序和最小的测试程序,向您展示了所有这些东西确实有效。你会注意到(这很好),这与我之前发布的代码没有变化(只是有点太聪明了):
// 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上运行。
好了。与你的非常不同(因为你走错了路,从此没有回头路)。我建议你带着它跑,祝你好运。
类Base
正在Interface
中实现函数,因此它应该从该类派生。则MyClass
仅继承自Base
:
class Base : public Interface {
public:
void f() {
cout << "Base::f()" << endl;
}
};
class MyClass : public Base {
public:
void g() {
cout << "MyClass::g()" << endl;
}
};
- 如何正确实现与基类不同的版本?
- 可以具有不同基成员实现的类的最佳实现
- 对派生自同一基类的类实现冲突检查
- 无法使用在子类中定义的虚拟getter实现基类
- 关于C++从派生类调用在基类中实现的虚拟函数的问题
- 如何在基类中定义静态接口,并确保该接口必须在派生类中实现
- 如何在CRTP实现中传递基类指针
- 有没有一种方法可以使用SFINAE来检测一个类型是否实现了给定的抽象基类
- C++ 多重继承:使用基类 A 的实现实现基类 B 的抽象方法
- 如何在子类中重写时调用私有虚拟基类实现
- 如何在基类中实现子类迭代器的统一接口?
- 调用基类克隆实现
- 检查实现哪些基类
- 我如何实现从其基类获取变量的构造函数
- 什么更有效率?在重载函数中或通过在基类函数中检查对象类型来实现
- 如何在抽象基类中实现运算符+
- 将多态性与运算符 + 重载模板化类结合使用.如何实现基类?
- 如何私下从基类的导数继承,但实现基类的公共接口?
- 从派生类实现基类构造函数专用化的替代方法是什么
- 实现基类方法来访问派生类的属性