簡體   English   中英

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繼承自BaseBase實現了自己的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()方法與按鈕無關,因此讓ButtonSender繼承沒有任何意義。
還有其他一些類可以實現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從它們兩者派生時,為什么在InterfaceBase都有一個稱為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派生它並實現OnPressOnRelease ),但是我會留給您一點。

最終,作為錦上添花,我讓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;
};

好,那里沒什么可看的。 請注意純虛擬方法OnPressOnRelease從該類派生的具體類都可以使用-以及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的指針,以便它可以在適當時調用其OnPressOnRelease方法。
  • STL的魔力(如果可用!)使ButtonList實現ButtonList微不足道。 它僅使用std::set在此處進行了說明 (請參見該頁面和鏈接頁面上的示例)。 按鈕來來去去添加和刪除自身。
  • PollAllButtons遍歷存儲在ButtonList所有Button * ,並調用我選擇為每個按鈕調用的對象, Poll依次調用關聯的ButtonHandlerOnPressOnRelease方法。 這使用一種稱為“范圍循環”的東西,它與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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM