polymorphic_downcast
头文件: "boost/cast.hpp"
有时 dynamic_cast
被认为太过低效(的确如此)。执行dynamic_cast
需要额外的运行时间。为了避免这些代价,常常会诱使你使用 static_cast
, 它没有这些性能代价。static_cast
用于向下转型可能在危险的,并会导致错误,但它的确比dynamic_cast
要快。如果这些加速是需要的,那我们就要确保向下转型的安全性。dynamic_cast
会测试向下转型的结果,并在失败时返回空指针或抛出异常,而static_cast
则仅仅执行需要的指针运算,并将保证转型有效的责任留给了程序员。为了确保用 static_cast
进行向下转型是安全的,你必须确保对每次要执行的转型进行测试。polymorphic_downcast
用dynamic_cast
进行了转型的测试,但仅是在调试模式下;然后它就使用 static_cast
去执行转型。在发布模式下,只执行 static_cast
。这样的转型方法意味着你知道它不可能失败,所以没有错误处理,也没有异常抛出。那么如果在非调试模式下 polymorphic_downcast
失败了,会发生什么呢?未定义的行为。你的计算机可能崩溃。地球可以停止自转。你可能飞到云上。你唯一可以肯定的是你的程序可能会发生不好的事情。如果 polymorphic_downcast
是在调试模式下失败的,它对dynamic_cast
产生的空指针执行断言(并退出)。
在讨论用polymorphic_downcast
更换dynamic_cast
可以如何加速你的程序之前,你应该先检查一下设计。转型的优化几乎就代表着设计的问题。如果向下转型真的是必须的,并且被证实是性能的瓶颈,polymorphic_downcast
就是你需要的。你可以在测试时发现错误的转型,而不是在产品中(发布模式构建),如果你曾经听到过从电话另一端传来的用户的尖叫,你就该知道在测试时找出错误是多么的重要,它使生活更轻松。很有可能你就是用户,而且知道发现并报告别人的错误是多么的讨厌。因此,在真正需要的时候才用 polymorphic_downcast
,而且要小心。
用法
polymorphic_downcast
用于那些你应该用而又不想用dynamic_cast
的情形,原因是你确认将要发生的转型肯定会成功,而且你需要提升它带来的性能。注意:一定要确保使用的polymorphic_downcast
所有可能的类型及转换组合都经过测试。否则,不要使用 polymorphic_downcast
; 用 dynamic_cast
代替它。当你决定继续使用polymorphic_downcast
, 包含头文件"boost/cast.hpp"
.
#include <iostream>
#include "boost/cast.hpp"
struct base {
virtual ~base() {};
};
struct derived1 : public base {
void foo() {
std::cout << "derived1::foo()\n";
}
};
struct derived2 : public base {
void foo() {
std::cout << "derived2::foo()\n";
}
};
void older(base* p) {
// Logic that suggests that p points to derived1 omitted
derived1* pd=static_cast<derived1*>(p);
pd->foo(); // <-- What will happen here?
}
void newer(base* p) {
// Logic that suggests that p points to derived1 omitted
derived1* pd=boost::polymorphic_downcast<derived1*>(p);
// ^-- The above cast will cause an assertion in debug builds
pd->foo();
}
int main() {
derived2* p=new derived2;
older(p); // <-- Undefined
newer(p); // <-- Well defined in debug build
}
函数older
中的static_cast
会编译成功,[6] 但它会带来坏运气,成员函数foo
的存在使得错误(可能有,但不保证)被错过,直到有人拿着一份错误报告,用调试器在别的地方查找奇怪的行为。当使用static_cast
将指针向下转型为 derived1*
, 编译器没有选择,只能相信程序员,转型是有效的。但事实上,传送给older
的指针是指向一个derived2
实例的。因此,older
里的指针 pd
指向了一个完全不同的类型,这意味着什么都可能发生。这就是使用static_cast
进行向下转型的风险。转型总是"成功"的,但指针可能是无效的。
[6] 至少它会被编译。
在对函数newer
的调用里,"更好的 static_cast
," polymorphic_downcast
不仅捕捉到了错误,并且使用断言指出了发生错误的地方。当然,这仅在调试模式下是真的,使用dynamic_cast
来测试转型是否成功。把一个无效的转型留在发布版本中会导致不幸。换言之,就算你在调试模式下获得了额外的安全性,但这并不足以代表你已经试过了所有可能的转换。
总结
使用static_cast
进行向下转换通常是危险的。你不应该这样做,但如果一定要,使用polymorphic_downcast
可以增加一点安全性。它在调试模式下增加了测试,可以帮助你发现转型的错误,但你必须测试所有可能的转型以确保它的安全使用。
如果你正在使用向下转型并需要在发布版本中获得
static_cast
的速度,就用polymorphic_downcast
; 至少在测试时你可以在出错时得到断言的帮助。如果不能测试所有可能的转型,就不要使用
polymorphic_downcast
.
记住这是一种优化方法,你应该在确定需要它们时才使用。