c++ - 如何正确组织继承类以利用多态性?

标签 c++ qt inheritance polymorphism

我正在尝试重新设计我的元素类别。我无法想象事情应该如何运作。

当前实现:

class Item : public QGraphicsItem
{
public:
    typedef enum  { PolygonType = QGraphicsItem::UserType + 1 } ShapeType;
    Item() {...} 
    Item(const Item &copyItem) // copy constructor
    {   m_shapeType = copyItem.getItemShape();
        m_color = copyItem.getItemColor();
        setPos(copyItem.pos()); ...}
    QRectF boundingRect() const;
    QPainterPath shape() const;
    void setItemShape(ShapeType s) { m_shapeType = s; }
    ShapeType getItemShape() const { return m_shapeType; }
    int type() const { return m_shapeType; }   // this will replace the above to allow QGraphicsItem casts
    void setItemColor(QColor c)    { m_color = c; }
    QColor getItemColor() const    { return m_color; }
    ....
protected:
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
               QWidget *widget);
    QColor m_color;
    ShapeType m_shapeType;
    ....
};

QPainterPath Item::shape() const
{
    QPainterPath path;
    switch (m_shapeType)
    {
    case Item::PolygonType:
        ...
        break;
    ....
    }
}
QRectF Item::boundingRect() const
{
    return QRectF(...);
}
void Item::paint(QPainter *painter, const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/)
{
    switch (m_shapeType)
    {
    case Item::PolygonType:
            painter->drawPolygon(shape().toFillPolygon());
            break;
    ....
    }
}

根据选择,我创建一个项目并添加到 Canvas 中...然后,完成所有更改后,我将其添加到 QGraphicsScene 中。

所需任务:

场景的处理包含类型的迭代

Item* item1 = new Item(*renderArea->item);
// make some changes on the item copy, then add it to a scene
collectionView->scene()->addItem(item1);

// in another function...
for(int i = 0; i < collectionView->scene()->items().size(); ++i)
{        
    Item* item = new Item(*(dynamic_cast<Item*>(
                           collectionView->scene()->items().at(i))));
   ...
}

我想要实现的目标

这是一个最小的方法,但我必须将功能扩展到扩展其他类的项目,例如QGraphicsPixmapItem(无需向每个不需要它的项目添加像素图)或QGraphicsSvgItem(同样,不必为不需要它的项目添加太多开销)。

所以我正在尝试:

class PolyItem : public Item
{
public:
    PolyItem(): Item()
    {
        m_shapeType = Item::PolygonType;
    }
    PolyItem(PolyItem& copy): Item::Item(copy)
    {
    }
    virtual void paint(QPainter* painter,
                       const QStyleOptionGraphicsItem* option,
                       QWidget* widget = NULL)
    {
        painter->drawPolygon(shape().toFillPolygon());
    }
};

class PixMapItem : public Item, public QGraphicsPixmapItem
{
public:
    PixMapItem(): Item()
    {
        m_shapeType = Item::PixmapType;
        setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
    }
    PixMapItem(PixMapItem& copy): Item::Item(copy)
    {
        setPixmap(copy.pixmap());
        setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
    }
    virtual void paint(QPainter* painter,
                       const QStyleOptionGraphicsItem* option,
                       QWidget* widget = NULL)
    {
        painter->drawPixmap(boundingRect(), pixmap());
    }
};

问题:

  • 我该如何让它发挥作用?当我选择形状时,如何创建 PolygonItem 的实例(并且仍然能够使用通用 Item 来修改其他属性,例如颜色?)

-----> 我正在考虑在类 Item 中创建一个不同的构造函数来创建正确的形状 - 但这似乎意味着 Item 需要类 Polygon,而该类需要类 Item... 循环依赖?

  • 更复杂的是,我如何执行上述任务、修改通用项目、迭代项目(无论形状如何)?

我无法想象的:

collectionView->scene()->addItem(item1);

如果我的项目是多边形类型,将调用 Item::paint() 函数 - 以及所有其他关联函数 - 或 PolyItem::paint() ?
我是否必须插入 switch-case 代码来确定和转换每种类型的每个实例?

最佳答案

也许值得将您的 Item 类简化为 BaseItem,其中包含您自己添加到 Item 的所有内容,以及您期望任何 Item 具有的所有功能(如果没有 QGraphics* 就无法实现,则作为纯虚拟):

class BaseItem {
public:
    void setItemColor(QColor c)    { m_color = c; }
    QColor getItemColor() const    { return m_color; }
    ....
protected:
    QColor m_color;
    ShapeType m_shapeType;
};

然后

class Item : public BaseItem, public QGraphicsItem
class PixMapItem : public BaseItem, public QGraphicsPixmapItem

等等 - 避免循环依赖。任何Item都可以称为BaseItem,并提供BaseItem定义的通用接口(interface)。每个子项都提供该接口(interface)的专门化以实现适当的 QGraphics*。

您原始设计的问题是该项目继承自 QGraphicsItem,但它不应该继承,因为您计划进一步将其专门化为 QGraphicsPixmapItem,它本身已经包含 QGraphicsItem。

编辑

现在,当您迭代它们时,您将所有项目视为 BaseItem - 这意味着 BaseItem 必须提供所有项目都遵守的接口(interface)。鉴于您在问题中提供的示例,一种方法是:

class BaseItem {
  virtual void AddToScene(QGraphicsScene* scene) = 0;
  // Caller takes ownership.
  virtual BaseItem* CreateCopy() = 0;
};

所需任务:

场景的处理包含类型的迭代

BaseItem* item1 = renderArea->item->CreateCopy();
// make some changes on the item copy, then add it to a scene
item1->AddToScene(collectionView->scene());

每个派生类都实现由 BaseItem 定义的接口(interface),而您在通用操作中将所有 Item 视为 BaseItem。这也使得代码易于扩展 - 添加新的 Item 类型只需要实现派生类,但现有代码保持不变 - 因此 BaseItem 需要仔细设计,为所有项目通用的操作提供接口(interface)。

class Item : public BaseItem, public QGraphicsItem {
      void AddToScene(QGraphicsScene* scene) override { scene->addItem(this); }
      // Caller takes ownership.
      BaseItem* CreateCopy() override { return new Item; }
};

关于c++ - 如何正确组织继承类以利用多态性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29949260/

相关文章:

c++ - 如何着手开发新的 Qt 5.7+ High-DPI Per Monitor DPI Aware 应用程序?

c++ - 我可以在基类中重载纯虚方法吗?

C++类成员变量交互

c++ - QT creator 不更新.ui 文件和设计器

qt - QQueue容器存在的目的是什么?

java - 继承和组合如何选择?

python - 从子类获取父私有(private)或 protected 值

c++ - 使用浮点/双除法比较可还原分数

c++ - 边界框和模型

c++ - 处理连接对象时出现段错误