C ++多重继承和访问说明符:从基类继承并派生

时间:2013-02-16 10:01:43

标签: c++ oop inheritance multiple-inheritance gtkmm

这是我遇到的一个意想不到的问题。我正在编写一个GUI应用程序,我使用GUI库中的两个类:Model类和View类。 Model类的内容由View类呈现给屏幕。在某些时候,我决定派生Model类,因为我需要扩展功能。图书馆的课程是派生出来的,我发现了许多例子。这很简单,工作得很好。

现在出现了一个问题:Model类提供了直接编辑模型数据的方法。我不希望这些方法公开,因为我写了包装器,这些包装器必须是唯一的编辑方式。我确实需要继承,因为我的派生MyModel会覆盖Model类的许多虚方法。所以我在想怎么做。这是一个包含所有细节的摘要:

  • 有一个BasicModel类,它提供了迭代模型数据的方法
  • 还有一个继承BasicModel的Model类,并通过提供编辑数据的方法来扩展它(我认为BaseModel是抽象的,没有数据,Model定义了内部数据结构并实现了由BasicModel)
  • 有一个继承Model的MyModel类。它覆盖了许多虚拟方法,具有扩展的编辑机制,并希望隐藏Model
  • 提供的低级编辑方法
  • 这是一个View类,它存储一个指向BasicModel对象的指针。因此,View仅使用迭代接口,甚至不知道任何编辑接口。

所以我想保持MyModel的迭代界面公开,这样我就可以将它传递给我的View对象,但是隐藏Model提供的编辑界面。

我想到的解决方案:

解决方案1:继承是不可避免的,因为我必须使用iverride虚拟方法,但解决方案本身并不必使用继承:我可以编写一个包装器来MyModel类,它提供对封装的MyModel对象的 const引用的访问,并为MyModel的编辑机制提供包装器。由于所有的包装器,它可能是丑陋的,但它避免了多个继承和访问说明符的混乱。

解决方案2:让MyModel继承Model。没有多重继承。现在转到MyModel.hpp中的MyModel类定义,并在protected ir private下编写所有Model编辑方法的方法声明,以便隐藏它们。这非常有效,但维护方面存在一个小问题:如果在库的未来版本中,模型接口会发生变化,例如:添加了一个新的编辑方法,我们必须将它作为私有/受保护的方法手动添加到MyModel类。当然,我可以跟踪库的更改,或至少转到其在线API参考并浏览Model类参考页面,只是为了确保没有任何更改,或者在必要时更新我的​​代码,在我的应用程序的生产/稳定版本发布之前。

解决方案3:使用多重继承,然后我不知道应该发生什么。是否安全。行为编译器是否依赖。这是一个想法:MyModel继承自Model和BasicModel:public继承自BasicModel(用于迭代接口)和protected/private继承自Model(为了隐藏Model'编辑界面)。

备注:

注1 :我的高级编辑机制使用模型的低级编辑方法。

注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派生,它完全解决了这个问题。但在我的情况下,我无法更改库类,因此它不是一个选项。

2 个答案:

答案 0 :(得分:1)

如果我理解你的问题,你应该使用混合继承(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中没有实际的状态(数据),除非有些东西我误解了再次 ......

答案 1 :(得分:0)

您可以执行类似

的操作
class MyModel : protected Model

{

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

如果我正确理解了这个案例,并且提供了模型不会从可迭代类中私下继承,我猜这里必须是这种情况。如果我说了些蠢话,请原谅我。我不是一个非常有经验的编码员。