在应用 Graphics View Framework 的时候,咱们少不了要对 QGraphicsItem 对象进行类型转换。我经验的尤为经典的场景就是在 QGraphicsView::mousePressEvent 中应用 QGraphicsView::itemAt 获取以后被选中的 item。而后对其进行类型转换之后再进行某些操作。

QGraphicsItem 只是一个基类,如果咱们想在 runtime 取得实在的子类类型,就必须对其进行类型转换。Type casting 相干的函数有泛滥抉择,有 C++ 的 static_castdynamic_cast 和 Qt 的 qobject_cast 。但遗憾的是这三个函数对 QGraphicsItem 的类型转换并不非常敌对。

先说说 dynamic_cast ,这个函数看似完满,而且失败也会返回 nullptr 。但咱们应尽量避免应用 RTTI。

而后就是 static_cast ,这个函数其实就是通过笼罩固定长度的内存进行类型转换。因而咱们并不能通过这种强转判断类型的转换是否胜利。

咱们无妨对 dynamic_caststatic_cast 做一些小试验。从试验1的运行后果能够看出,如果把 if statement 正文掉的话,程序会解体。起因是 cnullptr 。再来看看试验2的运行后果是输入 "b" ,调用的是类 B::hello,意料之外的后果。

class A{public:    virtual void hello()    {        qDebug() << a;    };private:    const char* a {"a"};};class B : public A{public:    void hello() override    {        qDebug() << b;    }private:    const char* b {"b"};};class C : public A{public:    void hello() override    {        qDebug() << c;    }private:    const char* c {"c"};};// 试验1int main(){    A* b = new B;    auto c = dynamic_cast<C*>(b);    // if (c != nullptr)    c->hello(); // crash!!!}// 试验2int main(){    A* b = new B;    auto c = static_cast<C*>(b);    c->hello(); // 输入 b}

最初就是 qobject_cast ,它能够说是 Qt 的亲儿子。但它并不能用于 QGraphicsItem,归根到底 QGraphicsItem 没有继承 QOject 。如果想要用 qobject_cast 的话,能够抉择 QGraphicsObject 作为基类。

除了上述的三种 type casting 函数, Qt 提供了 QGraphicsItem 系列专属的类型转换函数 qgraphicsitem_cast 。它的劣势在于,它并不应用 RTTI,而且转换失败的话会返回 nullptr

从源码能够看出,它的实现非常简略。通过判断 Item::Type 是否相等得出转换的后果。

template <class T> inline T qgraphicsitem_cast(QGraphicsItem *item){    typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type Item;    return int(Item::Type) == int(QGraphicsItem::Type)        || (item && int(Item::Type) == item->type()) ? static_cast<T>(item) : 0;}template <class T> inline T qgraphicsitem_cast(const QGraphicsItem *item){    typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type Item;    return int(Item::Type) == int(QGraphicsItem::Type)        || (item && int(Item::Type) == item->type()) ? static_cast<T>(item) : 0;}

事实上,这种 qgraphicsitem_cast 也有它的缺点。因为它是通过比拟 QGraphicsItem::Type 而决定类型转换是否胜利,这种做法有肯定的局限性,它只能用来判断两个子类是否继承于雷同的基类。

class A : public QGraphicsItem{    // ...};class B: public QGraphicsItem{    // ...};int main(){    auto a = new A;    auto b = new B;    qDebug() << a->type() == b->type(); // 输入 true    auto c = qgraphicsitem_cast<A*>(b);    // if语句成立    if (c != nullptr)        qDebug() << "true";}

如果想要获取确切的类型,请应用 dynamic_cast 。如果只须要判断是否继承于同一个基类,能够应用 qgraphicsitem_cast