在使用 Graphics View Framework 的时候,我们少不了要对 QGraphicsItem
对象进行类型转换。我经历的尤为经典的场景就是在 QGraphicsView::mousePressEvent
中使用 QGraphicsView::itemAt
获取当前被选中的 item。然后对其进行类型转换之后再进行某些操作。
QGraphicsItem
只是一个基类,如果我们想在 runtime 获得真实的子类类型,就必须对其进行类型转换。Type casting 相关的函数有众多选择,有 C++ 的 static_cast
、dynamic_cast
和 Qt 的 qobject_cast
。
先说说 dynamic_cast
,这个函数看似完美,而且失败也会返回 nullptr
。但我们应尽量避免使用 RTTI。
然后就是 static_cast
,这个函数其实就是通过覆盖固定长度的内存进行类型转换。因此我们并不能通过这种强转判断类型的转换是否成功。
我们不妨对 dynamic_cast
和 static_cast
做一些小实验。从实验1的运行结果可以看出,如果把 if statement 注释掉的话,程序会崩溃。原因是 c
为 nullptr
。再来看看实验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"};
};
// 实验1
int main()
{
A* b = new B;
auto c = dynamic_cast<C*>(b);
// if (c != nullptr)
c->hello(); // crash!!!
}
// 实验2
int 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
。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。