这是我遇到的一个意想不到的问题。我正在编写一个GUI应用程序,我使用GUI库中的两个类:Model类和View类。 Model类的内容由View类呈现给屏幕。在某些时候,我决定派生Model类,因为我需要扩展功能。图书馆的课程是派生出来的,我发现了许多例子。这很简单,工作得很好。
现在出现了一个问题:Model类提供了直接编辑模型数据的方法。我不希望这些方法公开,因为我写了包装器,这些包装器必须是唯一的编辑方式。我确实需要继承,因为我的派生MyModel会覆盖Model类的许多虚方法。所以我在想怎么做。这是一个包含所有细节的摘要:
所以我想保持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()
我们之一应该是私人的,因为私人继承,所以我不明白为什么它不能编译。最重要的是,假设它不是一个错误但只是我不理解事情是如何工作的 - 建议的多重继承解决方案是不可能的。解决这个问题的唯一方法是,如果我没弄错的话,就是虚拟继承,但我不能使用它,因为我使用的类是库类,他们不使用虚拟继承。即便如此,由于我一起使用私有和公共继承,它可能无法解决问题,仍然是一个暧昧的呼唤。
答案 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
}
如果我正确理解了这个案例,并且提供了模型不会从可迭代类中私下继承,我猜这里必须是这种情况。如果我说了些蠢话,请原谅我。我不是一个非常有经验的编码员。