假设我有 3 个类指针 classA 、 classB 和 ClassC 所有这些类都是从 QObject 派生的。我想知道是否有办法从基本类型中找出它们的派生类型。例如,如果我有这样的事情
void SomeMethod(QObject* ptr)
{
//How can I find out if a ClassA , ClassB or ClassC derived type was passed here ?
}
假设我有 3 个类指针 classA 、 classB 和 ClassC 所有这些类都是从 QObject 派生的。我想知道是否有办法从基本类型中找出它们的派生类型。例如,如果我有这样的事情
void SomeMethod(QObject* ptr)
{
//How can I find out if a ClassA , ClassB or ClassC derived type was passed here ?
}
这正是dynamic_cast
为了:
void SomeMethod(QObject *ptr) {
A *a = dynamic_cast<A *>(ptr);
if (a) { DoStuffA(a); return; }
B *b = dynamic_cast<B *>(ptr);
if (b) { DoStuffB(b); return; }
C *c = dynamic_cast<C *>(ptr);
if (c) { DoStuffC(c); return; }
DoStuffNOTA(ptr);
}
由于您使用的是 Qt,因此您可能希望使用,* 而qobject_cast
不是dynamic_cast
,否则一切都是一样的。
然而,所有这些重复通常都是你做错了什么的标志。您可以通过使用模板来消除很多它,但通常,您通常根本不想这样做。相反,使用“双重调度”(又名“反向调度”)模式。
我们拥有虚函数以及一般的 OO 的全部原因是为了避免类型切换。问题是虚函数(C++ 执行它们的方式)仅在this
对象上分派:Foo::SomeMethod(QObject *)
可以为 的子类覆盖Foo
,但不能为QObject
. 但是你可以添加一个方法到QObject
——或者,在这种情况下,添加到一个中间类(或者一个多继承混合,如果这不可能的话)——你所有的真实类都可以覆盖:
class ClassIntermediate: public QObject {
public:
virtual void DoSomeMethodStuff() = 0;
};
class ClassA : public ClassIntermediate {
public:
virtual void DoSomeMethodStuff() { /* DoStuffA code here */ }
// all the stuff you were already doing in ClassA as a QObject
};
// likewise for ClassB and ClassC
class Foo {
void SomeMethod(ClassIntermediate *ptr) { ptr->DoSomeMethodStuff(); }
};
如果所有类型都需要完成一些工作,请将其保留在SomeMethod
、 调用之前或之后DoSomeMethodStuff
。(如果该工作与子类特定的东西混合在一起,并且您无法重新排列它,只需将多个方法分解为双重分派,而不仅仅是一个。)
如果代码需要Foo
对象的this
指针,只需将其作为参数传递给DoSomeMethodStuff
.
如果您需要处理任何QObject
,而不仅仅是您的子类,那么您可能需要使用单次dynamic_cast<ClassIntermediate *>
和双次调度。
那么,为什么这比类型切换更好呢?今天,您有三个类,以及一个打开它们的方法。如果明天再增加一个方法,就得再写一次同样的3路类型开关。如果您随后添加了第四个类,您必须记住返回并编辑它们。如果你最终得到 N 个类和 M 个必须打开它们的方法,那么你已经编写了 N*M 个代码块而不是 N+M。所有这些额外的代码不仅令人讨厌。它几乎可以保证您有一天会错过其中的一个编辑,从而导致痛苦的调试问题。
的主要优点qobject_cast
是 Qt 提供了自己的、比 C++ 内置的更广泛的 RTTI 系统,并且完全可以在没有 C++ RTTI 的情况下构建 Qt(或者,在 Windows 上,以这样一种方式,不同 DLL 中的类可以通过 Qt RTTI 而不是 C++ RTTI 连接),并且qobject_cast
仍然可以工作。