如果包含的对象也继承,我如何定义相互继承的容器? (以QObject为基础)

时间:2013-01-18 14:56:09

标签: c++ visual-studio-2008 inheritance qobject

背景:

  • 我的班级名为ObjectListModel,其继承QAbstractListModel并包含QObjectList。对象是行,它们的属性是列(使用QMetaObject设置),并且通知更改将传播到视图。还有一些容器助手(开始/结束/迭代器/大小),以便我可以迭代存储的QObject。
  • 我还有TypedObjectListModel<T>,它提供了类型安全性(主要是通过覆盖push_back et.al.并定义iterator到{T}}的新static_cast类型)。

当我只有一种类型的物体时,这一切都很有效。我只是创建了一个新类(f.ex。FruitsModel,其中包含Q_OBJECT,并继承TypedObjectListModel<Fruit>。这只能包含Fruits或Fruit-subobjects。

但是,我现在有一个可以在两种不同状态下运行的应用程序。在第二种状态下,模型应该只包含苹果,没有香蕉(或者水果,这是一个具体的基类)。

所以,我想创建一个ApplesModel类型,它应该继承FruitsModel并只改变T的类型。这让我陷入困境,因为我得到了继承钻石死亡

 QObject
       |
 QAbstractListModel
       |
 ObjectListModel -------------------
       |                           |
 TypedObjectListModel<Fruit>     TypedObjectListModel<Apple>
       |                           |
 FruitsModel  -------------------ApplesModel

这在概念上也是错误的,因为FruitsModel :: push_back(Fruit *)在ApplesModel中是非法的。但是,读取/迭代水果(不仅仅是苹果)应该是可能的。

另外,我在FruitsModel(findFruitById)中有一些应该被覆盖的函数,只返回ApplesModel中的Apples。

在C ++中解决此问题的首选设计模式是什么?

我怀疑(希望)我不是第一个尝试做类似事情的人。

我尝试过很多想法,但我陷入了各种各样的死胡同。您认为ObjectListModel的虚拟继承可以解决问题,但后来我使用QObject::findChild得到了这个:

error C2635: cannot convert a 'QObject*' to a 'ApplesModel*'; conversion from a virtual base class is implied

以上可以通过我自己的findChild实现来解决,而不是使用dynamic_cast,但仍有一些死胡同。

template<typename T>
inline T myFindChild(const QObject *parent, const QString &name = QString())
{ 
    return dynamic_cast<T>(qt_qFindChild_helper(parent, name, reinterpret_cast<T>(0)->staticMetaObject)); 
}

更新

geekp有以下建议:

  

从Fruit继承Apple,不要打扰ApplesModel

我如何强制执行只有applesModel中的苹果?还有,我 每次我拿苹果(作为水果)都需要垂头丧气。

  

不要从FruitsModel继承(如果你不使用它,你为什么会这样做?   方法?)

我正在使用一些方法,尤其是阅读方法。

  

不要继承Apple的TypesObjectListModel,只继承FruitsModel。

与AppleModel无关的缺点。

2 个答案:

答案 0 :(得分:2)

因此,读取和写入操作在继承方面存在根本性的不同。

回到OOP 101,还记得关于方形和矩形的比喻吗?人们常说广场是一种矩形,但只有在阅读时才是这样。

写作时,正方形不是矩形,但矩形是各种正方形!

即:

bool test( Rectangle* r ) {
  int old_height = r->GetHeight();
  int old_width = r->GetWidth();
  r->SetWidth(old_width+100);
  return old_height == r->GetHeight();
}

上面的函数为所有“真实”矩形返回true,但对于Squares,它可能不会。因此SetWidth违反了Rectangle合理Square的合同。

另一方面,Rectangle的每个Square接口都由struct IRectangleRead { ... }; struct ISquareRead { ... }; struct ISquareWrite: virtual ISquareRead { ... }; struct IRectangleWrite:ISquareWrite, virtual IRectangleRead { ... }; struct ConstRectangle: virtual IRectangleRead { ... }; struct ConstSquare: virtual ISquareRead, virtual IRectangleRead { ... }; struct Rectangle: ConstRectangle, IRectangleWrite { ... }; struct Square: ConstSquare, ISquareWrite { ... }; 完美处理。

这可能会让你像这样一团糟:

{{1}}

生成一堆继承层次结构,但是可以在每个方法上放置限制性契约,并且实现该方法的每个对象都将遵循它们。

现在,您应该注意,如果您的对象是不可变的,上面的内容会变得非常简单。然后唯一的写作形式是通过工厂功能,事情变得整洁。

所以这里的具体课程 - 拆分阅读和修改代码的各个部分。公共修改部分(适用于基类)未公开,因为在子类情况下操作无效。

公共阅读部分是公开暴露的,子类型阅读部分也是如此。

子类型编写代码转发到私有公共基类编写代码。

答案 1 :(得分:0)

多种选择:

  • 从Fruit继承Apple并且不要打扰ApplesModel
  • 不要从FruitsModel继承(如果你没有使用它的方法,你为什么会这样做?)
  • 不要继承Apple的TypesObjectListModel,只继承FruitsModel