5

我看到一本关于 C++ 的书提到,使用静态转换导航继承层次结构比使用动态转换更有效。

例子:

#include <iostream>
#include <typeinfo>

using namespace std;

class Shape { public: virtual ~Shape() {}; };
class Circle : public Shape {};
class Square : public Shape {};
class Other {};

int main() {
    Circle c;

    Shape* s = &c; // Upcast: normal and OK

    // More explicit but unnecessary:
    s = static_cast<Shape*>(&c);
    // (Since upcasting is such a safe and common
    // operation, the cast becomes cluttering)

    Circle* cp = 0;
    Square* sp = 0;

    // Static Navigation of class hierarchies
    // requires extra type information:
    if(typeid(s) == typeid(cp)) // C++ RTTI
        cp = static_cast<Circle*>(s);
    if(typeid(s) == typeid(sp))
        sp = static_cast<Square*>(s);
    if(cp != 0)
        cout << "It's a circle!" << endl;
    if(sp != 0)
        cout << "It's a square!" << endl;

    // Static navigation is ONLY an efficiency hack;
    // dynamic_cast is always safer. However:
    // Other* op = static_cast<Other*>(s);
    // Conveniently gives an error message, while
    Other* op2 = (Other*)s;
    // does not
} ///:~

但是,动态转换和静态转换(如上面实现的)都需要启用 RTTI 才能使此类导航正常工作。只是动态转换需要类层次结构是多态的(即基类至少有一个虚函数)。
静态演员的这种效率提升从何而来?这本书确实提到动态转换是进行类型安全向下转换的首选方式。

4

4 回答 4

9

static_cast 本质上不需要 RTTI - 需要typeid(和 一样dynamic_cast),但这是一个完全不同的问题。大多数演员只是告诉编译器“相信我,我知道我在做什么” -dynamic_cast是例外,它要求编译器在运行时检查并可能失败。这就是最大的性能差异!

于 2009-09-05T05:37:00.880 回答
7

如果可能的话,最好避免打开类型。这通常是通过将相关代码移动到针对不同子类型实现不同的虚拟方法来完成的:

class Shape {
public:
    virtual ~Shape() {};
    virtual void announce() = 0;  // And likewise redeclare in Circle and Square.
};

void Circle::announce() {
    cout << "It's a circle!" << endl;
}

void Square::announce() {
    cout << "It's a square!" << endl;
}

// Later...
s->announce();

如果您正在使用无法更改的预先存在的继承层次结构,请研究访问者模式以获得更可扩展的类型切换替代方案。

更多信息: static_cast不需要RTTI,使用它的向下转换可能是不安全的,导致未定义的行为(例如崩溃)。 dynamic_cast安全但缓慢,因为它检查(因此需要)RTTI 信息。旧的 C 风格的转换比它会悄悄地转换完全不相关的类型更不安全,static_cast因为它会在static_cast编译时错误的对象中转换。

于 2009-09-05T05:36:41.057 回答
5

使用静态转换(和 typeid 检查),您不能向下转换为中间类型(孩子从父亲派生,从祖父派生,您不能从祖父向下转换为父亲),使用受到更多限制。没有 typeid 检查的 static_cast 会牺牲性能的正确性,然后你就知道他们说了什么:

为了表现而牺牲正确性的人不值得

当然,在某些情况下,您迫切需要一些 CPU 指令,并且没有其他地方可以寻求改进,而您实际上对自己正在做的事情是安全的,并且您已经测量(对吗?)这是唯一的地方获得性能是使用 static_cast 而不是 dynamic_cast...然后您知道您必须重新设计您的设计或您的算法或获得更好的硬件。

使用 rtti + static_cast 施加的限制是,您以后将无法使用新的派生类扩展您的代码,而无需重新处理您使用此技巧的所有地方,以获得一些 CPU 指令。返工本身可能需要比您获得的 CPU 时间更多的时间(更昂贵的工程时间)。无论如何,如果花在向下转换上的时间很明显,那么按照 j_random_hacker 的建议重新设计你的设计,它将在设计和性能上都得到改善。

于 2009-09-05T10:02:06.323 回答
1

dynamic_cast如果您没有进行 typeid 检查并且强制转换无法成功,则会返回 NULL。 static_cast会成功(并导致未定义的行为,例如最终的崩溃)。这可能是速度差异。

于 2009-09-05T05:32:13.120 回答