用于遍历继承层次结构的Static_cast与dynamic_cast

时间:2009-09-05 05:23:29

标签: c++ casting rtti dynamic-cast static-cast

我看过一本关于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 个答案:

答案 0 :(得分:9)

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

答案 1 :(得分: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();

如果您正在使用无法更改的预先存在的继承层次结构,请调查Visitor pattern以获得更易于替换的类型切换。

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

答案 2 :(得分:5)

使用静态强制转换(和typeid检查)你不能向下转换到一个中间类型(父亲派生的孩子来自祖父,你不能从爷爷堕落到父亲)的用法有点受限。没有typeid检查的static_cast牺牲了性能的正确性,然后你知道他们说了什么:

牺牲表现正确性的人不应该

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

使用rtti + static_cast所施加的限制是,您将无法在以后使用新派生类扩展代码,而无需重新处理使用此技巧获取少量CPU指令的所有位置。重新加工本身可能比您获得的CPU时间花费更多时间(工程时间更昂贵)。无论如何,如果用于向下投射的时间是显而易见的,那么按照j_random_hacker的建议重新设计你的设计,它将改善设计和性能。

答案 3 :(得分:1)

如果你没有做过typeid检查,那么

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