簡體   English   中英

C ++多重繼承和訪問說明符:從基類及其派生繼承

[英]C++ multiple inheritance and access specifiers: inherit from a base class and its derived

這是我遇到的意外問題。 我正在編寫一個GUI應用程序,並且使用GUI庫中的兩個類:Model類和View類。 Model的內容由View類呈現到屏幕上。 在某個時候,我決定派生Model類,因為我需要擴展功能。 該庫的類是派生的,我發現了許多示例。 這很容易並且完美地工作了。

現在有一個問題:Model類提供了直接編輯模型數據的方法。 我不希望這些方法公開,因為我寫了包裝器,而這些包裝器必須是唯一的編輯方法。 我確實需要繼承,因為派生的MyModel覆蓋了Model類的許多虛擬方法。 所以我在想該怎么辦。 以下是所有詳細信息的摘要:

  • 有一個BasicModel類,提供用於遍歷模型數據的方法
  • 有一個Model類,它繼承了BasicModel,並通過提供用於編輯數據的方法對其進行了擴展(我認為BaseModel是抽象的並且沒有數據,而Model定義了內部數據結構並實現了BasicModel提供的迭代接口)
  • 有一個MyModel類,它繼承了Model。 它覆蓋了許多虛擬方法,具有擴展的編輯機制,並且希望隱藏Model提供的較低級別的編輯方法。
  • 有一個View類,用於存儲指向BasicModel對象的指針。 因此,View僅使用迭代界面,甚至不知道任何編輯界面。

因此,我想公開MyModel的迭代接口,以便可以將其傳遞給View對象,但隱藏Model提供的編輯接口。

我想到的解決方案:

解決方案1:無法避免繼承,因為我必須使用虛方法,但是解決方案本身不必使用繼承:我可以為MyModel類編寫包裝器,該類提供對封裝的MyModel的const引用的訪問。對象,並為MyModel的編輯機制提供包裝。 由於所有的包裝器,這可能很丑陋,但它避免了多重繼承和訪問說明符的混亂。

解決方案2:讓 MyModel繼承Model。 沒有多重繼承。 現在轉到MyModel.hpp中的MyModel的類定義,並在protected ir private下編寫所有Model的編輯方法的方法聲明,以便將它們隱藏。 效果很好,但是維護方面存在一個小問題:如果在庫的未來版本中,Model接口發生了變化,例如添加了新的編輯方法,則我們必須將其作為私有/手動添加到MyModel類中。受保護的方法。 當然,我可以跟蹤庫的更改,或者至少可以轉到其在線API參考並瀏覽Model類參考頁面,以確保沒有任何更改,或者在必要時在生產/穩定版本的CD之前更新我的代碼。我的申請被釋放。

解決方案3:使用多重繼承,然后我不知道會發生什么。 是否安全? 行為編譯器是否依賴。 這就是想法:MyModel從Model和basicModel繼承:從BasicModel的public繼承(用於迭代接口),從Model的protected/private繼承(以隱藏模型的編輯接口)。

筆記:

注1 :我的高級編輯機制使用 Model的低級編輯方法。

注2 :虛擬方法MyModel覆蓋了它們,其中一些方法是由BasicModel定義的(因此也由Model繼承),而某些方法在BasicModel中不存在,而是由Model定義的(例如,與拖放相關的方法)。

注3:我使用的GUI庫是gtkmm ,是GTK +的C ++綁定,而我正在談論的類是Gtk :: TreeModel,Gtk :: TreeStore,Gtk :: TreeView和我自己的MyModel類,它們是從Gtk派生的:: TreeStore。 我忽略了這些名稱,因為該問題是一個通用的OO規划問題,但是我在這里提到的是真正的課程,以便熟悉它們的人可以更輕松地理解問題。

我不確定這里最好的設計。 無疑,解決方案3是維護成本最低的解決方案。 實際上是零,因為繼承訪問說明符只是自動完成所有工作。 問題是,解決方案3是否總是按預期工作,例如對於迭代方法,編譯器將其公開(由於從BasicModel繼承公共繼承)還是私有(由於從Model繼承私有,繼承自BasicModel)。 我從來沒有像這樣使用多重繼承...

偽代碼

庫或多或少是這樣工作的:

namespace GUI
{
     class BasicModel
     {
      public:
          /* iteration interface for observing the model */
          iterator get_iter();
          // but no data here, it's just an interface which calls protected virtual methods
          // which are implemented by deriving classes, e.g. Model
      protected:    
          virtual iterator get_iter_vfunc() = 0;
          virtual void handle_signal();
     };

     class Model : public BasicModel
     {
         /* inherits public iteration interface*/
         /* implements observation virtual methods from BasicModel*/
         virtual iterator get_iter_vfunc() { /*...*/ }
         /* has private data structures for the model data */
         std::vector<Row> data;
         /* has public editing interface, e.g. insert_row(), erase(), append()
         /* has protected drag-n-drop related virtual methods*/
         virtual void handle_drop();
     };
}

我的代碼:

class MyModel : public GUI::Model
{
     /* implements virtual methods from BasicModel, e.g. default signal handlers*/
     virtual void handle_signal() { /*...*/ }
     /* implements drag-n-drop virtual methods from Model*/
     virtual void handle_drop() { *...* }
     /* inherits public iteration interface*/
     /* inherits public editing interface (***which I want to hide***)
     /* implements its own editing mechanism*/
     void high_level_edit (int param);
};

嘗試GCC

我嘗試了以下代碼,在關閉警告的情況下進行了編譯(否則,GCC抱怨):

#include <iostream>

class Base
{
public:
    void func ()
    {
        std::cout << "Base::func() called" << std::endl;
        func_vfunc ();
    }
protected:
    virtual void func_vfunc ()
    {
        std::cout << "Base::func_vfunc() called" << std::endl;
    }
};

class Derived : public Base
{
protected:
    virtual void func_vfunc ()
    {
        std::cout << "Derived::func_vfunc() called" << std::endl;
    }
};

class MyClass : public Base, private Derived
{
};

int main (int argc, char* argv[])
{
    Base base;
    Derived derived;
    MyClass myclass;

    base.func ();
    derived.func ();
    myclass.func ();
    return 0;
}

由於某些原因,GCC堅持認為myclass.func()的調用是模棱兩可的,但是由於私有繼承,我們認為其中一個func()應該是私有的,所以我不明白為什么它不能編譯。 最重要的是,假設這不是一個錯誤,但只是我不了解事情是如何工作的-建議的多重繼承解決方案是不可能的。 如果我沒記錯的話,解決此問題的唯一方法是虛擬繼承,但是我不能使用它,因為我使用的類是庫類,並且它們不使用虛擬繼承。 而且即使那樣,由於我同時使用私有繼承和公共繼承,所以它可能無法解決問題,仍然是一個模棱兩可的電話。

編輯:我試圖使Derived和MyClass實際上從Base派生,它完全解決了這個問題。 但就我而言,我無法更改庫類,因此這不是一種選擇。

如果我正確理解了您的問題,則可能應該混合使用繼承(MyModel源自BaseModel)和組合(MyModel包含Model的私有實例)。

然后在MyModel中使用您自己的基本虛擬方法的實現,對於那些您不想重新實現的方法,只需使其成為相應的Model方法的代理即可。

無論如何,恕我直言,您應該避免多重繼承。 否則,隨着時間的流逝,它會變得很毛茸茸。


編輯:現在我可以看到代碼,我再給它一個鏡頭。

您需要做的是重新實現MyModelImpl類(來自Model的派生類)中需要的內容,該類將隱藏在代理類MyModel(BaseModel的派生類)中。 我的第一個想法非常相似,只是我不了解您需要重新實現Model的某些部分。

類似於以下內容:

class MyModel : public BaseModel {
public:
  void high_level_edit(int param) { m_impl.high_level_edit(param); }
protected:
  virtual iterator get_iter_vfunc() { return m_impl.get_iter_vfunc(); }
  virtual void handle_signal() { m_impl.handle_signal(); }

private:
  class MyModelImpl : public Model {
  public:
    void high_level_edit(int param);
    // reimplement whatever you need (methods present in BaseModel,
    // you need to call them from MyModel proxies)
    virtual iterator get_iter_vfunc() { /*...*/ }
  protected:
    // reimplement whatever you need (methods present only in Model,
    // you don't need to call them from MyModel proxies)
    virtual void handle_drop();
  };
  MyModelImpl m_impl;
};

我相信應該可以正常工作,因為BaseModel中沒有實際狀態(數據),除非再次誤解 ...

你可以做類似的事情

class MyModel : protected Model

{

public:
    using Model::iterator;
    //it will make iterator public in MyModel
}

如果我正確地理解了這種情況,並且假設Model沒有從可迭代的類中秘密繼承,那么我想這一定是這種情況。 如果我說些蠢話,請原諒我。 我不是一個非常有經驗的編碼員。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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