polymorphic_downcast

#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();
}

void newer2(derived1* 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
	return 0;
}

/*
derived1::foo()
Assertion failed: dynamic_cast<Target>(x) == x, file d:\boost_1_60_0\boost\polym
orphic_cast.hpp, line 94
请按任意键继续. . .
*/
    

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.

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


你可能感兴趣的:(polymorphic_downcast)