繁体   English   中英

多种继承和接口方法

[英]Multiple inheritance and interface methods

对于几个图形对象,我继承自QGraphicsLineItemQGraphicsRectItem等。

class CustomLine : public QGraphicsLineItem{};

class CustomRect : public QGraphicsRectItem{};

这些对象被添加到容器中,该容器是QGraphicsScene "scene"的自定义子类,用于显示这些项目并与这些项目进行交互。 this->scene->items()返回QGraphicItem的列表: QList<QGraphicsItem* >

我想要做的是使每个自定义对象类具有相同的自定义接口方法,例如setModeX() 然后我可以做类似的事情:

Foreach (BaseItem *item, this->scene->items()){
    item->setModeX(...);
}

但是我该如何实现呢? 如果我做一个像

class BaseItem{
    public: setModeX(); [...]
    private: Mode mode_;
} 

并继承

class CustomLine : public QGraphicsLineItem, BaseItem {};

因此,尽管场景仅应包含基于BaseItem项目(不确定此任务是否确实需要),但我首先要检索其2个基类之一(即QGraphicsItem的对象列表,并将其QGraphicsItem转换为其他对象基类BaseItem使用接口方法。 我可能无法在上面的循环中将CustomLine-item强制转换为BaseItem,因为它不了解其他基类。

编辑:

  • 我使用的是MinGW 4.8 32位(g ++)。

  • 我注意到,当我开始foreach循环时,场景中的项目消失了(但尚未看到原因)

应该可以正常工作,但至少在您想要多态行为的情况下,请记住在基类中声明虚拟接口方法。

在每个派生类中将要存在的函数声明为基类中的虚函数。 在基类中定义为虚函数的函数可以提供默认实现,但派生类可以随意重写该实现并提供自己的实现。 您还可以声明该函数“纯虚拟”,这意味着派生类必须为该特定函数提供一个实现。

无论哪种情况,当在基类的指针指向的实例化对象上调用该函数时,都会注意到基类将函数声明为虚函数,并使用虚表(vtable)查找要调用的函数(就像在基类的函数定义中一样(如果存在),或者函数的派生类版本)。

似乎您的问题上存在一些限制,这使问题变得比应有的多。 如果您无法控制scene的基础指针,但您知道每个项目都继承自BaseItem ,则可以在for循环中进行BaseItem

例如,使用上面的结构:

Foreach (QGraphicsItem *item, this->scene){
    ((BaseItem*) item)->setModeX(...);
}

当然,只有当您可以保证场景中的对象是从BaseItem派生的。

由于sceneQGraphicsScene ,因此它仅由QGraphicsItem组成。 因此,您不能如代码中所示直接将scene作为BaseItem进行迭代。 您必须遍历每个QGraphicsItem 您描述了可以向下转换到CustomLine ,然后向上转换回BaseItem ,但这仅在您知道scene中的所有项目都是线的情况下才有效。 如果scene包含其他类型的项目,则您的技术将要求您迭代每种项目,直到找到有效的下垂项目,然后将其投射回BaseItem

QGraphicsItem
          \         BaseItem
QGraphicsLineItem   /
            \      /
            CustomLine

如果Qt库在QGraphicsItem上使用了虚拟继承,那么将为您提供一个简单的解决方案。 然后,您只需要对BaseItem QGraphicsItem使用虚拟继承,然后向下转换就可以了。

           QGraphicsItem
            /        \
QGraphicsLineItem   BaseItem
            \        /
           CustomLineItem

由于Qt不会这样做,因此您可能需要对Qt库进行自定义更改,或者使用自己的转换机制进行编码。

假设您不愿意对Qt库本身进行自定义修改,那么一种方法是在Qt的QGraphicsItemBaseItem之间创建映射。 映射可以在BaseItem的构造函数中完成,并且可以从BaseItem的析构函数撤消。 为了使撤消映射更加有效,您还可以创建从BaseItemQGraphicsItem的链接。

class BaseItem {
    static std::unordered_map<QGraphicsItem *, BaseItem *> map;
    QGraphicsItem *link_;
public:
    BaseItem (QGraphicsItem *q) : link_(q) {
        //...
        map[q] = this;
    }
    virtual ~BaseItem () {
       map.erase(link_);
       //...
    }
    static BaseItem * getBaseItem (QGraphicsItem *q) {
        std::unordered_map<QGraphicsItem *, BaseItem *>::iterator i;
        if ((i = map.find(q)) == map.end()) return NULL;
        return i->second;
    }
    //...
};

//...
std::unordered_map<QGraphicsItem *, BaseItem *> BaseItem::map;

在你的派生类,你只需要通过thisBaseItem构造。

class CustomLine : public QGraphicsLineItem, public BaseItem {
public:
    CustomLine () : BaseItem(this) {
        //...
    };
    //...
};

然后,循环将使用静态BaseItem::getBaseItem()函数将QGraphicsItem指针转换为BaseItem指针。 因此,由于无法在QGraphicsItemBaseItem之间创建可用的继承关系,因此将它们的关系记录在表中。

QGraphicsItem <-----------------. link
          \         BaseItem <--' map
QGraphicsLineItem   /
            \      /
            CustomLine

CRTP解救:

template <class D> class Base {
  D& m_d;
public:
  Base(D& derived) : m_d(d) { ... }
  ...
};

class CustomLine : public QGraphicsLine, Base<CustomLine> {
  ...
  CustomLine() : Base(*this) { ... }
};

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM