🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## 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`. 记住这是一种优化方法,你应该在确定需要它们时才使用。