在运行时转换为派生类型

时间:2013-09-14 00:30:28

标签: c++

假设我有3个类指针classA,classB和ClassC所有这些类都是从QObject派生的。我想知道是否有办法让我从基本类型中找出它们的派生类型。对于Instance,如果我有这样的东西

void SomeMethod(QObject* ptr)
{
 //How can I find out if a ClassA , ClassB or ClassC derived type was passed here ?

}

1 个答案:

答案 0 :(得分:3)

这正是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,*但其他方面都是相同的。

但是,所有这些重复通常都表明你做错了什么。你可以通过使用模板消除很多,但通常,你通常根本不想这样做。相反,使用"double dispatch"(又名“反向调度”)模式。

我们拥有虚函数和OO的一般原因是避免类型切换。问题是虚函数(C ++执行它们的方式)只在this对象上调度:Foo::SomeMethod(QObject *)可以覆盖Foo的子类,但不能覆盖QObject的子类}。但是你可以添加一个方法QObject - 或者,在这种情况下,添加到一个中间类(或多重继承mixin,如果这是不可能的) - 你所有真正的类都可以覆盖:< / p>

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提供了自己的,更广泛的RTTI系统,而不是内置于C ++的系统,并且可以在没有C ++ RTTI的情况下构建Qt(或者在Windows上,在这种情况下)不同DLL中的类可以通过Qt RTTI而不是C ++ RTTI连接的方式,qobject_cast仍然可以工作。

相关问题