Boost的转换函数是对C++中的四种类型转换函数(const_cast,reinterpret_cast,static_cast,dynamic_cast)的一些补充和扩展,在阅读本文前,请先熟悉C++中的四种类型转换函数相关知识。
polymorphic_castC++提供了dynamic_cast来实现运行时的类型转换,但是如果用来转换指针时,需要记得检查返回值(这是很多程序员容易忘掉的地方),否则一旦转换失败,将获得一个NULL指针,无异于给程序埋下了一个定时炸弹。
Boost的polymorphic_cast在dynamic_cast的基础上增加了对返回值的检测,如果转换失败,它就会抛出std::bad_cast异常。其函数体如下:
template <class Target, class Source>
inline Target polymorphic_cast(Source* x BOOST_EXPLICIT_DEFAULT_TARGET)
{
Target tmp = dynamic_cast<Target>(x);
if ( tmp == 0 ) throw std::bad_cast();
return tmp;
}
虽然抛异常增加了开销,但使用起来却更加简单了。
polymorphic_downcast由于抛出异常会降低程序的效率,而且dynamic_cast更会查询一个type_info结构来确定正确的类型,所以不管是空间上的成本还是时间上的成本,都会大大增加。在一些应用场景中,只需要在编译期间进行类型转换即可。这时我们可以使用static_cast来实现编译期间的类型转换,但static_cast可能导致错误的类型转换:
struct A
{
virtual ~A(){}
};
class B:public A{};
class C:public A{};
int main()
{
A *pa = new C();
B *pb = static_cast<B*>(pa);
}
对于上述程序,虽然pa和pb间没有继承关系,但是这个转换却可以通过,运行时也不会报任何错误,可一旦对pb进行访问,就会得到错误的结果甚至直接导致程序死掉。
polymorphic_downcast就巧妙的解决的这一问题,首先还是先看看它的定义:
template <class Target, class Source>
inline Target polymorphic_downcast(Source* x BOOST_EXPLICIT_DEFAULT_TARGET)
{
BOOST_ASSERT( dynamic_cast<Target>(x) == x ); // detect logic error
return static_cast<Target>(x);
}
从它的定义可以看出,在运行Release模式下,它和是static_cast一样的,也就是说它的Release版具有和static_cast一样的开销。但在Debug模式下,它会首先进行一次动态转换,而一旦类型不匹配,就会抛出异常。
在上述程序中,如果用polymorphic_downcast来替换static_cast的话,我们可以先在Debug模式下运行程序,如果有错误的类型转换,将很容易的检测出来。待改正所有的错误后,再发布Release版,这样即没有动态转换造成的开销,又杜绝了错误的类型转换。
boost::numeric_cast在c++中,我们经常需要把不同类型的数字互相转换,如将一个数字在long和short之间转换。但由于各数字的精度不同,当一个数字从"大"类型到"小"类型就可能导致转换失败,如下所示:
long n1 = 99999999;
short n2 = static_cast<short>(n1);
对于如上转换,n2得到的是一个负数,显然这个不是我们所期望的,并且这种运行时的错误是很难检测的,一旦使用了这个错误的转换后的数据,后果不堪设想。
boost::numeric_cast可以帮助我们解决这一问题,对于上面的转换,boost::numeric_cast会抛出一个boost:: bad_numeric_cast这个异常对象。从而保证转换后值的有效性。上述代码可以改写为如下:
try
{
long n1 = 99999999;
short n2 = boost::numeric_cast<short>(n1);
}
catch(boost::bad_numeric_cast&)
{
std::cout<<"The conversion failed"<<std::endl;
}
numeric_cast是如何知道这样的数字转换失败的呢?numeric_cast合理的应用了std::numeric_limits<>,而std::numeric_limits<>就是内建数字类型的type_tratis。当然也可以将自己定义的数字抽象类型添加到std::numeric_limits<>的特化版本中,这样numeric_cast就可以作用于自定义的类型了。由于相对复杂点,本文是介绍其功能和用法,就不分析其源码了,感兴趣的朋友可以参看boost文档和代码。
对于numeric_cast的使用也是有些要求的。
源类型和目标类型必须都是可拷贝构造的源类型和目标类型必须都是数字型类型。也就是被std::numeric_limits<>::is_specialized的特化定义为true源类型必须能被static_cast转换为目标类型其实对我们用的系统内置的数字来说,这几条都不是限制,只有我们在需要通过它转换自定义的数据类型时,才需要注意,否则编译不通过(其实这个错误还比较好发现和解决)。
boost::lexical_cast在C/C++程序开发中,往往需要将数字型对象的值转换为字符文本格式,或反之操作。虽然C语言就提供了不少系统函数来进行这种操作,如scanf、atoi等。这些函数小巧简洁,使用很方便,但缺少扩展性。在std中引入了stringstream来以一个通用的方式实现各种转换,但缺少对错误转换的检测。而boost::lexical_cast是在stringstream上的一个扩展,增加了对错误的类型转换的检测:
#include <string>
#include <iostream>
#include <boost/lexical_cast.hpp>
using namespace std;
int main()
{
try
{
int i = 100;
string str = boost::lexical_cast<string>(i);
cout<<"The string is:"<<str<<endl;
str = "error";
i = boost::lexical_cast<int>(str);
}
catch(boost::bad_lexical_cast& exobj)
{
cout<<"Convert err:"<<endl;
cout<<exobj.what()<<endl;
}
}
在上述转换中,第二个转换从"err"到int的转换是失败的,会抛出一个boost::bad_lexical_cast的异常,从而能帮助我们构造更安全稳定的程序。
boost::lexical_cast内部实现其实也是一个stringstream的封装,其函数简化如下:
template<typename Target,typename Source>
Target lexical_cast(Source arg){
detail::lexical_stream<Target,Source> interpreter;
Target result;
if(!(interpreter<<arg && interpreter>>result))
throw_exception(bad_lexical_cast(typeid(Target),typeid(Source)));
return result;
}
其中lexical_stream<>对字符串流做了一系列的包装,主要提供了operator<<(Source)和operator>>(Target)操作,用于判断操作是否成功。