開門見山 廢話少說<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
一、與多態類型相關的轉換
1、 polymorphic_cast與dynamic_cast
dynamic_cast可以安全地將一個指向多態對象的指針向下轉換爲派生類指針。但是,當dynamic_cast轉換失敗時,返回的是NULL,也就是說,dynamic_cast的轉換成功與否是在運行期確定,而不像其他C++內建cast那樣在編譯期確定。換言之,如果在進行dynamic_cast之後,不檢測返回值,就等於埋下了一個定時炸彈。
而Boost的polymorphic_cast在dynamic_cast的基礎上增加了對返回值的檢測,如果轉換失敗,它就會拋出std::bad_cast。
2、 polymorphic_downcast與dynamic_cast
但是拋出異常會降低程序的效率,而且dynamic_cast更會查詢一個type_info結構來確定正確的類型,所以不管是空間上的成本還是時間上的成本,都會大大增加。
polymorphic_downcast可以把成本降低到最低,甚至是編譯期上。不過使用這個轉換必須非常的小心。
//#define NDEBUG
#include<cassert>
using namespace std;
struct A{
virtual ~A(){}
};
class B:public A{};
class C:public A{};
int main(){
A *pa=new C;
B *pb=polymorphic_downcast<B*>(pa);
}
注意上面沒有定義NDEBUG(或編譯器的調式模式),這樣才能在調試時,判斷polymorphic_downcast是否成功。最後,當一切OK的時候,再將定義NDEBUG(或關閉編譯器的調試模式)。這樣在最終的軟件中,polymorphic_downcast即保證了安全的轉換,又保證了低運行成本。
3、 窺其內部
template <typename Target, typename Source>
inline Target polymorphic_cast(Source*x){
Target tmp = dynamic_cast<Target>(x);
if ( tmp == 0 ) throw std::bad_cast();
return tmp;
}
polymorphic_cast本質上還是一個dynamic_cast。
現在來關心一下polymorphic_downcast
template <typename Target, typename Source>
inline Target polymorphic_downcast(Source* x){
assert( dynamic_cast<Target>(x) == x );
return static_cast<Target>(x);
}
可以看到polymorphic_downcast裏用到了assert。當在調試模式下,assert將起到非常關鍵的作用。如果dynamic_cast轉換成功,那麼static_cast必然不會出現問題。當在調試模式下一切就緒後,關閉assert的功能,那麼就可以保證最後在static_cast下也不會出現問題。這樣就把運行成本降低了。
二、與數字相關的轉換
1、numeric_cast與static_cast和隱式轉換
先來看一個將double類型的整數轉換爲int類型的數static_cast的轉換。
double d=10;
int i=static_cast<int>(d); //int i(d);
對於這個轉換看不出任何的缺陷,不過唯一可以肯定的是double是8Bytes,而int只有4Bytes,這也說明了轉換後的值極有可能不正確。例如嘗試把d的值改爲9999999999,你就會發現轉換後i的值變得極爲畸形。雖然這樣的轉換算得上是失敗的轉換,但是並不是所有從“大”類型到“小”類型的轉換都是失敗的轉換,也不是所有從“小”類型到“大”類型的轉換都是合格的轉換。
Boost的numeric_cast可以幫我們解決這樣的問題。例如對於上面的,當d爲9999999999,那麼轉換必將失敗,numeric_cast就拋出boost:: bad_numeric_cast這個異常對象。這樣,就能保證轉換後值的有效性。用法見下。
double d=10;
int i;
try{
i=boost::numeric_cast<int>(d);
}
catch(boost::bad_numeric_cast&){
std::cout<<”The conversion failed”<<std::endl;
}
對於numeric_cast的使用是有要求的。
1、 源類型和目標類型必須都是可拷貝構造的
2、 源類型和目標類型必須都是數字型類型。也就是被std::numeric_limits<>::is_specialized的特化定義爲true
3、 源類型必須能被static_cast轉換爲目標類型
2、窺其內部
numeric_cast是如何知道這樣的數字轉換失敗的呢?numeric_cast合理的應用了std::numeric_limits<>,而std::numeric_limits<>就是內建數字類型的type_tratis。當然也可以將自己定義的數字抽象類型添加到std::numeric_limits<>的特化版本中,這樣numeric_cast就可以作用於自定義的類型了。
template<typename Target, typename Source>
inline Target numeric_cast(Source arg){
typedef detail::fixed_numeric_limits<Source> arg_traits;
typedef detail::fixed_numeric_limits<Target> result_traits;
if (
(arg<0 && !result_traits::is_signed) //條件一
|| (arg_traits::is_signed && arg < result_traits::min()) //條件二
|| arg > result_traits::max()) ) //條件三
{
throw bad_numeric_cast();
}
return static_cast<Target>(arg);
} // numeric_cast
條件一:如果源類型對象的當前值小於0,而且目標類型是無符號型(unsigned)的,那麼轉換失敗。
條件二:如果源類型對象是有符號型的,而且它的當前值小於目標類型所容納的最小值,那麼轉換失敗。
條件三:如果源類型對象的當前值大於目標類型的最大值,那麼轉換失敗。
如果這三個條件都不滿足,那麼就可以放心大膽的用static_cast轉換這個數字類型。
//(還沒完,繼續)