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

C++ C++;多重继承和访问说明符:从基类及其派生类继承,c++,oop,inheritance,multiple-inheritance,gtkmm,C++,Oop,Inheritance,Multiple Inheritance,Gtkmm,这是我遇到的一个意想不到的问题。我正在编写一个GUI应用程序,我使用GUI库中的两个类:一个模型类和一个视图类。模型内容由视图类呈现到屏幕上。在某个时候,我决定派生模型类,因为我需要扩展功能。这个库的类是用来派生的,我发现了很多例子。这很简单,工作也很完美 现在有一个问题:Model类提供了直接编辑模型数据的方法。我不希望这些方法公开,因为我编写了包装,而这些包装必须是编辑的唯一方式。我确实需要继承,因为我的派生MyModel覆盖了Model类的许多虚拟方法。所以我在想该怎么办。以下是一份包含所

这是我遇到的一个意想不到的问题。我正在编写一个GUI应用程序,我使用GUI库中的两个类:一个模型类和一个视图类。模型内容由视图类呈现到屏幕上。在某个时候,我决定派生模型类,因为我需要扩展功能。这个库的类是用来派生的,我发现了很多例子。这很简单,工作也很完美

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

  • 有一个BasicModel类,它提供了通过模型数据进行迭代的方法
  • 有一个模型类,它继承了BasicModel,并通过提供编辑数据的方法对其进行了扩展(我认为BaseModel是抽象的,没有数据,模型定义了内部数据结构并实现了BasicModel提供的迭代接口)
  • 有一个MyModel类继承了这个模型。它覆盖了许多虚拟方法,扩展了编辑机制,并希望隐藏模型提供的低级编辑方法
  • 有一个视图类,它存储指向BasicModel对象的指针。因此,视图只使用迭代接口,甚至不知道任何编辑接口
所以我想保持MyModel的迭代接口是公共的,这样我就可以将它传递给我的视图对象,但是隐藏了Model提供的编辑接口

我想到的解决方案:

解决方案1:无法避免继承,因为我必须继承虚拟方法,但解决方案本身不必使用继承:我可以编写MyModel类的包装器,它提供对封装的MyModel对象的常量引用的访问,并提供对MyModel编辑机制的包装器。由于所有的包装器,它可能很难看,但它避免了与多重继承和访问说明符的混淆

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

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

注意事项:

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

注2:虚拟方法MyModel重写,其中一些由BasicModel定义(因此也由Model继承),一些不存在于BasicModel中,由Model定义(例如与拖放相关的方法)

注3:我使用的GUI库是<强> GTKMM ,C++绑定的<强> GTK+<强>,我所讨论的类是GTK::TrimeDell,GTK:::TreeStore,GTK::TreVIEW和我自己的MyMead类,它来自GTK::TreeStore。我忽略了这些名称,因为这个问题是一个一般的OO规划问题,但我在这里提到的是真实的类,这样熟悉它们的人将更容易理解这个问题

我不确定这里最好的设计是什么。显然,解决方案3是维护成本最低的解决方案。它实际上是零,因为继承访问说明符只是自动完成所有工作。问题是,解决方案3是否总是像预期的那样工作,例如,对于迭代方法,编译器会将其公开(因为从BasicModel进行公共继承)还是私有(因为从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();
     };
}
使用GCC进行试验

我尝试了以下代码,编译时关闭了警告(否则GCC会抱怨):

#包括
阶级基础
{
公众:
void func()
{

如果我正确理解了您的问题,您可能应该混合使用继承(MyModel派生自BaseModel)和组合(MyModel包含Model的私有实例)

然后在MyModel中使用您自己的基本虚拟方法实现,对于那些您不想重新实现的方法,只需将它们作为相应模型方法的代理

无论如何,如果可以的话,我想你应该避免多重继承。否则,随着时间的推移,它会变得非常棘手


编辑:现在我可以看到代码了,我再给它一次机会

您需要做的是重新实现MyModelImpl(派生自Model)类中您需要的任何内容,该类将隐藏在代理类MyModel(派生自BaseModel)中。我的第一个想法非常相似,只是我不明白您需要重新实现模型的某些部分

大致如下:

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;
};
<
#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;
}
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;
};
class MyModel : protected Model

{

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