偷窺Boost Conversion Library(一)

開門見山 廢話少說<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

一、與多態類型相關的轉換

1、 polymorphic_castdynamic_cast

dynamic_cast可以安全地將一個指向多態對象的指針向下轉換爲派生類指針。但是,當dynamic_cast轉換失敗時,返回的是NULL,也就是說,dynamic_cast的轉換成功與否是在運行期確定,而不像其他C++內建cast那樣在編譯期確定。換言之,如果在進行dynamic_cast之後,不檢測返回值,就等於埋下了一個定時炸彈。

Boostpolymorphic_castdynamic_cast的基礎上增加了對返回值的檢測,如果轉換失敗,它就會拋出std::bad_cast

2、 polymorphic_downcastdynamic_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下也不會出現問題。這樣就把運行成本降低了。

 

二、與數字相關的轉換

1numeric_caststatic_cast和隱式轉換

先來看一個將double類型的整數轉換爲int類型的數static_cast的轉換。

double d=10;

int i=static_cast<int>(d); //int i(d);

對於這個轉換看不出任何的缺陷,不過唯一可以肯定的是double8Bytes,而int只有4Bytes,這也說明了轉換後的值極有可能不正確。例如嘗試把d的值改爲9999999999,你就會發現轉換後i的值變得極爲畸形。雖然這樣的轉換算得上是失敗的轉換,但是並不是所有從“大”類型到“小”類型的轉換都是失敗的轉換,也不是所有從“小”類型到“大”類型的轉換都是合格的轉換。

Boostnumeric_cast可以幫我們解決這樣的問題。例如對於上面的,當d9999999999,那麼轉換必將失敗,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轉換這個數字類型。

 

//(還沒完,繼續)

發佈了32 篇原創文章 · 獲贊 15 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章