Beyond the C++ Standard Library: An Introduction to Boost -- Library 2.3 polymorphic_downcast

polymorphic_downcast

头文件: "boost/cast.hpp"

有时 dynamic_cast 被认为太过低效(的确如此)。执行dynamic_cast需要额外的运行时间。为了避免这些代价,常常会诱使你使用 static_cast, 它没有这些性能代价。static_cast 用于向下转型可能在危险的,并会导致错误,但它的确比dynamic_cast要快。如果这些加速是需要的,那我们就要确保向下转型的安全性。dynamic_cast 会测试向下转型的结果,并在失败时返回空指针或抛出异常,而static_cast 则仅仅执行需要的指针运算,并将保证转型有效的责任留给了程序员。为了确保用 static_cast 进行向下转型是安全的,你必须确保对每次要执行的转型进行测试。polymorphic_downcastdynamic_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.

记住这是一种优化方法,你应该在确定需要它们时才使用。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值