1

这是我遇到的一个意想不到的问题。我正在编写一个 GUI 应用程序,我使用 GUI 库中的两个类:一个模型类和一个视图类。模型内容由 View 类呈现到屏幕上。在某些时候,我决定派生 Model 类,因为我需要扩展功能。图书馆的类是派生的,我发现了很多例子。这很容易而且效果很好。

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

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

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

我想到的解决方案:

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

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

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

笔记:

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

注2:MyModel 覆盖的虚方法,其中一些是由BasicModel 定义的(因此也由Model 继承),而另一些在BasicModel 中不存在,由Model 定义(例如与拖放相关的方法)。

注3:我使用的GUI库是gtkmm ,是GTK+的C++绑定,我说的类是Gtk::TreeModel、Gtk::TreeStore、Gtk::TreeView和我自己的MyModel类,它派生自Gtk ::树店。我忽略了这些名称,因为这个问题是一个通用的 OO 规划问题,但我在这里提到了真正的类,以便熟悉它们的人更容易理解这个问题。

我不确定这里最好的设计是什么。毫无疑问,解决方案 3 是维护成本最低的解决方案。它实际上为零,因为继承访问说明符只是自动完成所有工作。问题是,解决方案 3 是否总是按预期工作,例如对于迭代方法,编译器会将其设为公共(因为从 BasicModel 继承的公共继承)还是私有(因为从派生自 BasicModel 的 Model 的私有继承)。我从来没有像这样使用多重继承......

伪代码

这或多或少是图书馆的工作方式:

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 派生,它完全解决了问题。但就我而言,我无法更改库类,所以这不是一个选择。

4

2 回答 2

1

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

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

无论如何,恕我直言,如果可以的话,您应该避免多重继承。否则随着时间的推移它会变得非常多毛。


编辑:现在我可以看到代码,我再试一次。

您需要做的是在 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 中没有实际状态(数据),除非我再次误解了一些东西......

于 2013-02-16T10:17:45.943 回答
0

你可以做类似的事情

class MyModel : protected Model

{

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

如果我正确理解了这种情况,并且提供的 Model 不是从可迭代类私下继承的,我猜这里一定是这种情况。如果我说了一些愚蠢的话,请原谅我。我不是一个非常有经验的编码器。

于 2016-11-04T12:43:13.593 回答