[英]C++ Multiple Inheritance: Using base class A's implementation for abstract method of base class B
我有一個抽象類Interface
,它聲明f()
和g()
方法。
MyClass
實現Interface::g()
,而MyClass
繼承自Base
, 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()
方法與按鈕無關,因此讓Button
從Sender
繼承沒有任何意義。
還有其他一些類可以實現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;
}
};
為什么隨后在MyClass
從它們兩者派生時,為什么在Interface
和Base
都有一個稱為f
的函數? 您在此處創建了名稱沖突,因此遇到了麻煩。 好的,所以這次您可以使用三行方法擺脫它。 但是下次呢? 接下來呢? 不,不要去那里。
C ++是不是Java -它沒有接口。 相反,正如您所發現的那樣,派生自抽象基類的具體類(或類鏈-使用單一繼承)需要實現該類聲明的所有純虛方法。
您在此處嘗試做的事情與語言精神背道而馳,很可能使您陷入困境,因此您需要調整自己的思維方式。
多重繼承,特別是當在一個或多個基類中聲明相同的方法或數據成員名稱時,通常是一個較差的設計選擇,並且通常表現為症狀,而不是類層次結構設計中固有的錯誤,因此我建議重新考慮一下-您會很高興的。
無論如何,現在您知道了為什么您
會
被拒絕(盡管不是我本人)。 我希望您覺得這很有用-很好。 我不必寫這個,但是我想再寫一次,我在SO上經常看到這種事情,我感到很痛苦。
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;
}
};
謝謝OP,您發布了一個更具體的示例。 這促使我采取了不尋常的步驟來發布第二個答案。 希望對你有幫助。
我在這里要做的是向您顯示一個替代的類層次結構,希望您將看到該層次結構更適合您要解決的問題。 我將只關注Button
類(這是根本問題所在)與我稱之為ButtonHandler
。
現在ButtonHandler
可以是任何東西- Button
肯定不關心它是什么-它只是需要在按下或釋放它所擁有的按鈕通知。 一旦意識到這是問題的核心,解決方案就變得更加顯而易見。 當然,您可以將這種方法應用於Sender
類(只需從ButtonHandler
派生它並實現OnPress
和OnRelease
),但是我會留給您一點。
最終,作為錦上添花,我讓Button
s將自己添加/刪除到您正在談論的列表中,以便可以通過某種循環輕松地對它們進行輪詢。
好了,走吧。 讓我們從我們的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
的指針 , 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
方法。 ButtonList
實現ButtonList
微不足道。 它僅使用std::set
, 在此處進行了說明 (請參見該頁面和鏈接頁面上的示例)。 按鈕來來去去添加和刪除自身。 PollAllButtons
遍歷存儲在ButtonList
所有Button *
,並調用我選擇為每個按鈕調用的對象, Poll
依次調用關聯的ButtonHandler
的OnPress
和OnRelease
方法。 這使用一種稱為“范圍循環”的東西,它與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上運行它。
所以你去了。 與您的完全不同(因為您出發的路線錯誤,並且無法從那里退回)。 我建議您隨身攜帶它,祝您好運。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.