2017年4月12日 星期三

Operator 隱式轉換的重載,物件的轉出與轉入

Operator 隱式轉換的重載,物件的轉出與轉入

tags: operator2
你可以把一個新的物件當作一個型態看待,就像int是一個整數型態裡面什麼都沒有就只有一個成員int,這樣的概念,我們可以自訂一個型態。
這個型態你可以決定用什麼方式初始化他
class Opcast {
public: //建構子
    Opcast(int i): i(i) {}
private:
    int num;
};
比如說妳決定創建一個屬於自己的整數型態,可以這樣建立,且你可以從主程式初始化。
int main(int argc, char const *argv[]){
    Opcast a(int(1));
    return 0;
}
你也可以選擇輸入他類型的變數,比如說 double,在輸入的時候轉換成int讓引數符合初始化參數。static_cast<int>(num) 你可以把它看成 (int)num ,但前者是更好的寫法。
int main(int argc, char const *argv[]){
    double num = 1.0;
    Opcast a(static_cast<int>(num));
    return 0;
}
這裡我提一個自己的類比概念轉入,簡而言之就知把另一個型態轉入這個型態。還有一個概念是轉出,比如說這裡的 (int)num 以int的型態轉出給main使用。
你可能已經注意的到括號的使用右邊轉入,左邊轉出。
int i=0;
cout << Opcast(i) << endl;

Opcast a;
cout << (int)a << endl;
前者是把 i 導入,後者則是把 a 以 int 型態導出,這個以int形式導出的 行別轉換函式 是一個operator,這可能比較難想像。
函式的樣貌長這個樣子
Opcast::operator int() const{
    return i;
}
看你想重載導出什麼樣貌就寫什麼型態,後面的const是為了讓它可以同時接收兩種型態的呼叫
Opcast a(0);
const Opcast b(0);
這個樣子看起來一切很很美好,但實際上轉出與轉入它潛藏著不容易被發現的危機

轉入的隱式轉換

如果你有一個副程式接收了妳的自訂類別,而且那個自訂類別參數只有一個
Opcast::Opcast(int i): num(i) {}

void fun(Opcast a){
    cout << "fun" << endl;
}
我們試著呼叫他
fun(1);
理應是找不到定義為整數的方程式
void fun(int);
而實際上,編譯器會想辦一切辦法奏和出恰當的轉換,那個呼叫會變成
fun( Opcast(1) );
產生的結果可能不是你想要的,你可以透過關鍵字 explicit 避免這種編譯器自作主張的轉換
explicit Opcast::Opcast(int i): num(i) {}

傳統的解決方案(C11之前沒有這個關鍵字),可以使用替身類別來處理
class OpcastSize{
public:
    OpcastSize(int i): len(i){}
    operator int&(){return len;}
    int len;
};

Opcast::Opcast(OpcastSize i): num(i) {}


轉出的隱式轉換

如果你加載了型別轉換的函式要注意它有可能在你不需要的時候自作主張的呼叫
    Opcast a(1);
    cout << a << endl;
看起來因該會因為你沒有加載 << 運算子而導致呼叫失敗,實際上因為你加載了型別轉換函式,編譯器將會發現經過型別轉換後就可以正常印出了,而自作主張的幫你轉換。
這裡一樣可以使用關鍵字 explicit 來避免這種狀況
explicit operator double() const{...}
傳統的方案是乾脆不寫了,另外單獨使用一個普通函式來轉出,可以觀察 string 裡面的函式 c_str() 來進行型別的轉換。
這樣的寫法比較明確不會有其他可能的誤解,誤會成我是否可以將他轉成其他的任意型態,比如說 int、double。
其次也可以減少代碼的長度,C++的正確轉型方式為 static_cast<T>(t) 非常的長呢。


範例代碼

 /*****************************************************************
Name : Operator 隱式轉換的重載
Date : 2017/04/11
By   : CharlotteHonG
Final: 2017/04/11
*****************************************************************/
#include <iostream>
using namespace std;

class Opcast {
public:
    class OpcastSize{
    public:
        OpcastSize(int i): len(i){}
        operator int&(){return len;}
        int len;
    };
public: //建構子
    // ##轉入會被隱式轉換
    Opcast(int i): num(i) {}
    // 解決方法 1
    // explicit Opcast(int i): num(i) {}
    // 解決方法 2
    // Opcast(OpcastSize i): num(i) {}
public: // 轉型函式
    operator double() const{
        cout << "Cast to double. --> ";
        return num+1;
    }
    double to_dou(){
        cout << "Cast to double. --> ";
        return num+1;
    }
private:
    int num;
};

void fun(Opcast a){
    cout << "fun" << endl;
}
/*==============================================================*/
int main(int argc, char const *argv[]){
    Opcast a(1);
    // 轉入的隱式轉換
    cout << a << endl;
    // 轉出的隱式轉換
    fun(10);
    return 0;
}
/*==============================================================*/

沒有留言:

張貼留言