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()
成为 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
是一个抽象 class Sender
具有与发送 MIDI 消息相关的不同方法,例如update()
(检查是否应该发送消息,必要时发送),setAddress()
更改消息地址。消息是 MIDI 事件。我必须更改 address/channel 以实现移调和选择不同库等功能。
Base
class是classButton
。它有一个方法 update()
检查按钮是否被按下,并相应地调用(虚拟)方法 press()
和 release()
。
MyClass
然后会实现 press()
和 release()
方法,以便在按钮状态改变时发送正确的数据。
还有其他类型的 Sender
,我将它们全部保存在一个列表中以一次性 update()
它们,而不管 Sender
的具体类型。我还需要能够更改列表中所有 Sender
的地址(一次全部)。
setAddress()
方法与按钮无关,所以Button
继承自Sender
没有意义。
还有其他 classes 可以实现 Button
,但它们并不都是 Sender
s。
也许我必须完全改变结构,但目前我真的没有看到更好的解决方案。
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;
}
};
为什么你在 Interface
和 Base
中都有一个名为 f
的函数,而你随后在 MyClass
中从这两个函数派生?你在那里制造了一个名字冲突,因此你的困难。好的,所以这次您可以使用三行方法来摆脱它。但是下次呢?下一个?不,不要去那里。
C++ 不是 Java - 它 没有 接口。相反,从抽象基础 class 派生的具体 class(或 classes 链 - 使用单一继承)需要实现 all如您所见,class 声明的纯虚方法。
你在这里试图做的事情违背了语言的精神,很可能会给你带来麻烦,所以你需要调整你的想法。
多重继承,尤其是在一个或多个基 classes 中声明相同的方法或数据成员名称时,通常是一种糟糕的设计选择,并且更多时候是症状而不是设计中固有的错误首先是 class 层次结构,所以我的建议是重新审视它 - 你会很高兴你这样做了。
无论如何,现在你知道为什么你 被否决了(尽管不是我)。我希望你觉得这很有用——它是用心良苦的。我不必写这个,但又一次,我想,我在 SO 上经常看到这种事情,我觉得很痛苦。
感谢 OP 发布更具体的示例。它促使我采取不寻常的步骤发布第二个答案。我希望你觉得它有用。
我在这里要做的是向您展示另一种 class 层次结构,我希望您会看到它更适合您要解决的问题。我将重点关注 class Button
(这是根本问题所在)与我将称之为 ButtonHandler
.[=53= 的东西之间的关系]
现在 ButtonHandler
可以是任何东西 - Button
当然不关心它是什么 - 它只需要在 它拥有的按钮 [=86] 时得到通知=] 被按下或释放。一旦您意识到这是问题的核心,解决方案就会变得更加明显。当然,您可以将此方法应用于 Sender
class(只需从 ButtonHandler
派生并实现 OnPress
和 OnRelease
),但我会留给你吧。
最后,作为锦上添花的一点,我让我的 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;
};
好的,那里没什么可看的。请注意纯虚方法 OnPress
和 OnRelease
- 用于从这个派生的具体 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
的指针,以便它可以在适当的时候调用其 OnPress
和 OnRelease
方法。
- STL 的魔力(如果可用!)使
ButtonList
的实现变得微不足道。它只是使用一个
std::set
,记录在案 here(请参阅该页面和链接页面上的示例)。按钮在列表中来来去去时添加和删除自己。
PollAllButtons
遍历存储在 ButtonList
中的所有 Button *
并为每个调用我选择调用的 Poll
,后者又调用关联的 ButtonHandler
s OnPress
和 OnRelease
适当的方法。这使用了一种叫做 "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。
好了。与你的截然不同(因为你走错了路,没有回头路)。我建议你带上它 运行 带上它,祝你好运。
我有一个抽象 class 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
是一个抽象 class Sender
具有与发送 MIDI 消息相关的不同方法,例如update()
(检查是否应该发送消息,必要时发送),setAddress()
更改消息地址。消息是 MIDI 事件。我必须更改 address/channel 以实现移调和选择不同库等功能。
Base
class是classButton
。它有一个方法 update()
检查按钮是否被按下,并相应地调用(虚拟)方法 press()
和 release()
。
MyClass
然后会实现 press()
和 release()
方法,以便在按钮状态改变时发送正确的数据。
还有其他类型的 Sender
,我将它们全部保存在一个列表中以一次性 update()
它们,而不管 Sender
的具体类型。我还需要能够更改列表中所有 Sender
的地址(一次全部)。
setAddress()
方法与按钮无关,所以Button
继承自Sender
没有意义。
还有其他 classes 可以实现 Button
,但它们并不都是 Sender
s。
也许我必须完全改变结构,但目前我真的没有看到更好的解决方案。
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;
}
};
为什么你在 Interface
和 Base
中都有一个名为 f
的函数,而你随后在 MyClass
中从这两个函数派生?你在那里制造了一个名字冲突,因此你的困难。好的,所以这次您可以使用三行方法来摆脱它。但是下次呢?下一个?不,不要去那里。
C++ 不是 Java - 它 没有 接口。相反,从抽象基础 class 派生的具体 class(或 classes 链 - 使用单一继承)需要实现 all如您所见,class 声明的纯虚方法。
你在这里试图做的事情违背了语言的精神,很可能会给你带来麻烦,所以你需要调整你的想法。
多重继承,尤其是在一个或多个基 classes 中声明相同的方法或数据成员名称时,通常是一种糟糕的设计选择,并且更多时候是症状而不是设计中固有的错误首先是 class 层次结构,所以我的建议是重新审视它 - 你会很高兴你这样做了。
无论如何,现在你知道为什么你 被否决了(尽管不是我)。我希望你觉得这很有用——它是用心良苦的。我不必写这个,但又一次,我想,我在 SO 上经常看到这种事情,我觉得很痛苦。
感谢 OP 发布更具体的示例。它促使我采取不寻常的步骤发布第二个答案。我希望你觉得它有用。
我在这里要做的是向您展示另一种 class 层次结构,我希望您会看到它更适合您要解决的问题。我将重点关注 class Button
(这是根本问题所在)与我将称之为 ButtonHandler
.[=53= 的东西之间的关系]
现在 ButtonHandler
可以是任何东西 - Button
当然不关心它是什么 - 它只需要在 它拥有的按钮 [=86] 时得到通知=] 被按下或释放。一旦您意识到这是问题的核心,解决方案就会变得更加明显。当然,您可以将此方法应用于 Sender
class(只需从 ButtonHandler
派生并实现 OnPress
和 OnRelease
),但我会留给你吧。
最后,作为锦上添花的一点,我让我的 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;
};
好的,那里没什么可看的。请注意纯虚方法 OnPress
和 OnRelease
- 用于从这个派生的具体 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
的指针,以便它可以在适当的时候调用其OnPress
和OnRelease
方法。 - STL 的魔力(如果可用!)使
ButtonList
的实现变得微不足道。它只是使用一个std::set
,记录在案 here(请参阅该页面和链接页面上的示例)。按钮在列表中来来去去时添加和删除自己。 PollAllButtons
遍历存储在ButtonList
中的所有Button *
并为每个调用我选择调用的Poll
,后者又调用关联的ButtonHandler
sOnPress
和OnRelease
适当的方法。这使用了一种叫做 "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。
好了。与你的截然不同(因为你走错了路,没有回头路)。我建议你带上它 运行 带上它,祝你好运。