【问题标题】:Casting to derived type during Runtime在运行时转换为派生类型
【发布时间】:2013-09-14 00:30:28
【问题描述】:

假设我有 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 ?

}

【问题讨论】:

  • 您或许可以尝试 RTTI。但是我只会在基类中创建一个纯虚函数,它为每个类返回一个不同的标识并打开它。或者你可以使用 boost::variant 或 boost::any。
  • 构建您自己的定制 RTTI 来进行类型切换并不比仅使用您已经拥有的 RTTI 更好。事实上,在大多数方面情况都更糟——更多的代码、更多的 CPU 工作,最重要的是,还有更多出错的机会。
  • 顺便提一下,在这种特殊情况下,您可以使用 QObject 的重要自省功能。此外,对于 QObject,您应该使用 qobject_cast 而不是 dynamic_cast。
  • @MatteoItalia:好点。我更新了我的答案以包括后半部分。对于前半部分......除了强制转换或显式类型切换之外,Qt 内省并不能做很多事情,因为这就是 OP 想要的所有功能。但无论如何,这绝对值得指出。

标签: c++


【解决方案1】:

这正是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——或者,在这种情况下,添加到一个中间类(或多继承混合,如果这不可能的话)——您的所有真实类都可以覆盖:

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&lt;ClassIntermediate *&gt; 并对其进行双重调度。

那么,为什么这比类型切换更好?今天,您有三个类,以及一个打开它们的方法。如果明天再增加一个方法,就得再写一次同样的3路类型开关。如果您随后添加了第四个类,您必须记住返回并编辑它们。如果你最终得到 N 个类和 M 个必须打开它们的方法,那么你已经编写了 N*M 个代码块而不是 N+M。所有这些额外的代码不仅令人讨厌。它几乎可以保证您有一天会错过其中一个编辑,从而导致痛苦的调试问题。


qobject_cast 的主要优势在于 Qt 提供了自己的、比 C++ 内置的更广泛的 RTTI 系统,并且完全可以在没有 C++ RTTI 的情况下构建 Qt(或者,在 Windows 上,以这种方式不同 DLL 中的类可以通过 Qt RTTI 而不是 C++ RTTI 连接),qobject_cast 仍然可以工作。

【讨论】:

    猜你喜欢
    • 2022-10-14
    • 1970-01-01
    • 1970-01-01
    • 2010-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多