C++語言是一種強類型語言,當咱們須要用一種類型的對象來替代另外一種類型的對象進行相關操做時,必須首先進行類型轉換。在C++語言中,類型轉換有兩種方式,隱式類型轉換和顯示類型轉換。今天咱們就來聊一聊這些類型轉換的具體意義和應用特色。html
在有隱式類型轉換的表達式中,使用者不須要明確指定一個類型的對象該轉換爲另外哪個類型,這個工做將隱含地由編譯器來完成,編譯器將分析表達式的含義,進行類型轉換。程序員
隱式類型轉換針對不一樣的類型有不一樣的轉換方式,整體能夠分爲兩種類型,算術類型和類類型。express
算術類型轉換的設計原則就是儘量避免損失精度。安全
具體地,有如下幾條參考規則:函數
在C++中,能夠經過定義單參數構造函數和轉換函數來進行類類型轉換,這種方式也稱爲用戶定義的轉換(User-Defined Conversion)。這種方式在使用的時候不須要指明轉換類型,而是由編譯器自動進行選擇轉換函數,因此也是一種隱式類型轉換。優化
用戶定義的類類型轉換能夠由編譯器自動執行,可是編譯器每次只能執行一種類類型的轉換。若是一個對象須要從類型A自動轉換爲類型C,則類型A必須有直接轉換爲類型C的函數,不容許類型A轉換爲類型B,而後類型B轉換爲類型C。.net
若是一個類的某個構造函數只接受一個參數,且沒有被聲明爲explicit,則它實際上定義了將這個參數的類型轉換爲此類類型的隱式轉換機制,咱們把這種構造函數稱爲轉換構造函數。在轉換構造函數中只容許一步類類型的轉換。設計
最多見的例子就是將C類型字符串轉換爲string類型,例如 string s = "hello world",由於string類有一個構造函數string(const char *s),因此"hello world"字符串能夠自動轉換爲一個string類型的臨時變量,而後將這個臨時變量的值複製到s中。指針
咱們能夠自定義一個類類型以下:code
class Sales_data { public: Sales_data() = default; Sales_data( const std::string &s ) : bookNo( s ) { } Sales_data( const std::string &s, unsigned n, double p ) : bookNo( s ), units_sold( n ), revenue( p*n ) { } Sales_data( std::istream & ); std::string isbn() const { return bookNo; } Sales_data& combine( const Sales_data& ); private: std::string bookNo; unsigned units_sold; double revenue; };
在上述例子中,因爲Sales_data( const std::string &s );構造函數的存在,所以存在string類型到Sales_data的轉換,所以在須要Sales_data對象的時候,咱們可使用string類型替代。
Sales_data item; string null_book = "9-999-99999-9"; item.combine( null_book );
只有一次的隱式類類型轉換是可行的,而item.combine( "9-999-99999-9" )是錯誤的,由於在該語句中,存在着兩次隱式轉換,一次是字符串常量"9-999-99999-9"到string的轉換,另外一次是string到Sales_data的轉換。
explicit constructors:
若是你不想隱式轉換,以防用戶誤操做怎麼辦?
能夠經過將構造函數聲明爲explicit來阻止隱式轉換。explicit構造函數只能用於直接初始化。不容許編譯器執行其它默認操做(好比類型轉換,賦值初始化)。
關鍵字explicit只對一個實參的構造函數有效。
在類類型轉換中,咱們一般有兩個需求,一個是將其餘類型的數據轉換爲咱們自定義類的類型,另外一個是將自定義類的類型在須要的時候轉換爲其餘的數據類型。轉換構造函數能很好地知足前一個需求,針對後面一個需求,咱們除了可使用普通的成員函數進行顯示轉換,在C++中,還可使用類型轉換函數進行隱式轉換。
類型轉換函數的做用就是將一個類的對象轉換成另外一類型的數據。
咱們常常寫下述代碼:
while( cin >> num ){ ... }
輸入操做符 >> 是二元操做符,返回作操做數做爲其表達式結果,所以cin >> num返回cin,然而cin是輸入流istream的對象,該對象能出如今條件表達式中,是由於在istream中定義了類型轉換函數 operator bool();
類型轉換函數通常形式:
operator 目標類型() { ... return 目標類型數據; }
例如:
class A { public: A(const int x) : _x(x) {} operator int() { return _x; } private: int _x; }; int main () { A a(10); int res = a + 20; std::cout << res << std::endl; }
輸出結果爲30.
因爲類A定義了從該類對象到int類型的轉換函數,因此在進行整數算術運算的時候,能夠直接使用該類對象,由於編譯器會隱式地調用該類型轉換函數將類對象轉換爲整型數據。
顯示類型轉換就是在表達式中明確指定將一種類型轉換爲另外一種類型。隱式類型轉換通常是由編譯器進行轉換操做,顯示類型轉換是由程序員寫明要轉換成的目標類型。顯示類型轉換又被稱爲強制類型轉換。
C風格的強制轉換很簡單,無論什麼類型的轉換均可以使用使用下面的方式。
type val = (type)(expression);
固然,C++支持C風格的強制轉換,可是C風格的強制轉換可能帶來一些隱患,讓一些問題難以察覺。
C++提供了4個命名的強制類型轉換,它們都有以下的基本形式:
type val = cast-name<type>(expression);
cast-name是static_cast、dynamic_cast、const_cast、reinterpret_cast中的一種。在這裏簡單探討一下。
static_cast 很像 C 語言中的舊式類型轉換。它能進行基礎類型之間的轉換,也能將帶有可被單參調用的構造函數或用戶自定義類型轉換操做符的類型轉換,還能在存有繼承關係的類之間進行轉換(便可將基類轉換爲子類,也可將子類轉換爲基類),還能將 non-const對象轉換爲 const對象(注意:反之則不行,那是const_cast的職責)。
static_cast 轉換時並不進行運行時安全檢查,因此是非安全的,很容易出問題。所以 C++ 引入 dynamic_cast 來處理安全轉型。
dynamic_cast 主要用來在繼承體系中的安全向下轉型,是實現多態的一種方式。
它能安全地將指向基類的指針轉型爲指向子類的指針或引用,並獲知轉型動做成功是否。若是轉型失敗會返回null(轉型對象爲指針時)或拋出異常(轉型對象爲引用時)。dynamic_cast會使用運行時信息(RTTI)來進行類型安全檢查,所以dynamic_cast存在必定的效率損失。(我曾見過屬於優化代碼80/20法則中的20那一部分的一段遊戲代碼,起初使用的是dynamic_cast,後來被換成 static_cast 以提高效率,固然這僅是權宜之策,並不是好的設計。)
前面提到 const_cast 可去除對象的常量性(const),它還能夠去除對象的易變性(volatile)。const_cast 的惟一職責就在於此,若將 const_cast 用於其餘轉型將會報錯。
reinterpret_cast 用來執行低級轉型,如將執行一個 int 的指針強轉爲 int。其轉換結果與編譯平臺息息相關,不具備可移植性,所以在通常的代碼中不常見到它。reinterpret_cast 經常使用的一個用途是轉換函數指針類型,便可以將一種類型的函數指針轉換爲另外一種類型的函數指針,但這種轉換可能會致使不正確的結果。總之,reinterpret_cast 只用於底層代碼,通常咱們都用不到它,若是你的代碼中使用到這種轉型,務必明白本身在幹什麼。
以上內容部分來自網上其餘的博客,參考: