关于c ++:Static_cast与dynamic_cast用于遍历继承层次结构

Static cast vs. dymamic cast for traversing inheritance hierarchies

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

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#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才能使此类导航生效。 只是动态转换需要类层次结构是多态的(即具有至少一个虚函数的基类)。
静态铸件的效率增益来自何处?
该书确实提到动态演员是进行类型安全向下转换的首选方式。


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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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会以编译时错误为对象。


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

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

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

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


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