遍历继承层次结构的静态强制转换与动态强制转换

Ank*_*kur 5 c++ dynamic-cast casting rtti 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
} ///:~
Run Code Online (Sandbox Code Playgroud)

但是,动态强制转换和静态强制转换(如上所述)都需要启用RTTI才能使此类导航生效.只是动态转换需要类层次结构是多态的(即基类具有至少一个虚函数).
静态铸件的效率增益来自何处?该书确实提到动态演员是进行类型安全向下转换的首选方式.

Ale*_*lli 9

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

  • @ankur,在上面的代码中使用`typeid`非常具体 - 它只检查2种类型.如果有任何进一步的子类,则typeid检查将失败.`dynamic_cast`更通用(特别是因为它需要处理MULTIPLE继承,无论你是否在任何地方使用它!),即使进一步的子类化发生它也能正常工作,所以它可能不会令人惊讶慢点! (4认同)

j_r*_*ker 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();
Run Code Online (Sandbox Code Playgroud)

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

更多信息: static_cast要求RTTI,但使用它向下转换可能是不安全的,导致不确定的行为(例如崩溃). dynamic_cast是安全的但很慢,因为它检查(因此需要)RTTI信息.旧的C风格的转换更不安全,static_cast因为它会悄悄地抛出完全不相关的类型,在static_cast这些类型中会出现编译时错误.


Dav*_*eas 5

使用静态强制转换(和typeid检查)你不能向下转换为中间类型(父亲来自父亲,孩子来自祖父,你不能从祖父传给父亲)使用有点受限.没有typeid检查的static_cast牺牲了性能的正确性,然后你知道他们说了什么:

牺牲表现正确性的人也不值得

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

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