1. 程式人生 > >C++型別轉換函式

C++型別轉換函式

用轉換建構函式可以將一個指定型別的資料轉換為類的物件。但是不能反過來將一個類的物件轉換為一個其他型別的資料(例如將一個Complex類物件轉換成double型別資料)。

C++提供型別轉換函式(type conversion function)來解決這個問題。型別轉換函式的作用是將一個類的物件轉換成另一型別的資料。如果已聲明瞭一個Complex類,可以在Complex類中這樣定義型別轉換函式:
operator double( )
{
   return real;
}

型別轉換函式的一般形式為
operator 型別名( )
{
   實現轉換的語句
}

在函式名前面不能指定函式型別,函式沒有引數。其返回值的型別是由函式名中指定的型別名來確定的。型別轉換函式只能作為成員函式,因為轉換的主體是本類的物件。不能作為友元函式或普通函式。
從函式形式可以看到,它與運算子過載函式相似,都是用關鍵字operator開頭,只是被過載的是型別名
。double型別經過過載後,除了原有的含義外,還獲得新的含義(將一個Complex類物件轉換為double型別資料,並指定了轉換方法)。這樣,編譯系統不僅能識別原有的double型資料,而且還會把Complex類物件作為double型資料處理。

那麼程式中的Complex類對具有雙重身份,既是Complex類物件,又可作為double型別資料。Complex類物件只有在需要時才進行轉換,要根據表示式的上下文來決定。轉換建構函式和型別轉換運算子有一個共同的功能: 當需要的時候,編譯系統會自動呼叫這些函式,建立一個無名的臨時物件(或臨時變數)。
例1 使用型別轉換函式的簡單例子。
#include <iostream>
using namespace std;
class Complex
{
   public:
   Complex( ){real=0;imag=0;}
   Complex(double r,double i){real=r;imag=i;}
   operator double( ) {return real;} //型別轉換函式
   private:
   double real;
   double imag;
};

int main( )
{
   Complex c1(3,4),c2(5,-10),c3;
   double d;
   d=2.5+c1;//要求將一個double資料與Complex類資料相加
   cout<<d<<endl;
   return 0;
}


對程式的分析:
  1. 如果在Complex類中沒有定義型別轉換函式operator double,程式編譯將出錯。
  2. 如果在main函式中加一個語句:
       c3=c2;
    由於賦值號兩側都是同一類的資料,是可以合法進行賦值的,沒有必要把c2轉換為double型資料。
  3. 如果在Complex類中聲明瞭過載運算子“+”函式作為友元函式:
    Complex operator+ (Complex c1,Complex c2)//定義運算子“+”過載函式
    {
       return Complex(c1.real+c2.real, c1.imag+c2.imag);
    }
    若在main函式中有語句
       c3=c1+c2;
    由於已對運算子“+”過載,使之能用於兩個Complex類物件的相加,因此將c1和c2按Complex類物件處理,相加後賦值給同類物件c3。如果改為
       d=c1+c2; //d為double型變數
    將c1與c2兩個類物件相加,得到一個臨時的Complex類物件,由於它不能賦值給double型變數,而又有對double的過載函式,於是呼叫此函式,把臨時類物件轉換為double資料,然後賦給d。

從前面的介紹可知: 對型別的過載和本章開頭所介紹的對運算子的過載的概念和方法都是相似的。過載函式都使用關鍵字operator。因此,通常把型別轉換函式也稱為型別轉換運算子函式,由於它也是過載函式,因此也稱為型別轉換運算子過載函式(或稱強制型別轉換運算子過載函式)。

假如程式中需要對一個Complex類物件和一個double型變數進行+,-,*,/等算術運算,以及關係運算和邏輯運算,如果不用型別轉換函式,就要對多種運算子進行過載,以便能進行各種運算。這樣,是十分麻煩的,工作量較大,程式顯得冗長。如果用型別轉換函式對double進行過載(使Complex類物件轉換為double型資料),就不必對各種運算子進行過載,因為Complex類物件可以被自動地轉換為double型資料,而標準型別的資料的運算,是可以使用系統提供的各種運算子的。

例2 包含轉換建構函式、運算子過載函式和型別轉換函式的程式。
先閱讀以下程式,在這個程式中只包含轉換建構函式和運算子過載函式。
#include <iostream>
using namespace std;
class Complex
{
   public:
   Complex( ){real=0;imag=0;}  //預設建構函式
   Complex(double r){real=r;imag=0;}//轉換建構函式
   Complex(double r,double i){real=r;imag=i;}//實現初始化的建構函式
   friend Complex operator + (Complex c1,Complex c2); //過載運算子“+”的友元函式
   void display( );
   private:
   double real;
   double imag;
};
Complex operator + (Complex c1,Complex c2)//定義運算子“+”過載函式
{
   return Complex(c1.real+c2.real, c1.imag+c2.imag);
}
void Complex∷display( )
{
   cout<<″(″<<real<<″,″<<imag<<″i)″<<endl;
}
int main( )
{
   Complex c1(3,4),c2(5,-10),c3;
   c3=c1+2.5; //複數與double資料相加
   c3.display( );
   return 0;
}


對程式的分析:
  1. 如果沒有定義轉換建構函式,則此程式編譯出錯。
  2. 現在,在類Complex中定義了轉換建構函式,並具體規定了怎樣構成一個複數。由於已過載了算符“+”,在處理表達式c1+2.5時,編譯系統把它解釋為
       operator+(c1,2.5)
    由於2.5不是Complex類物件,系統先呼叫轉換建構函式Complex(2.5),建立一個臨時的Complex類物件,其值為(2.5+0i)。上面的函式呼叫相當於
       operator+(c1,Complex(2.5))
    將c1與(2.5+0i) 相加,賦給c3。執行結果為
       (5.5+4i)
  3. 如果把“c3=c1+2.5;”改為c3=2.5+c1; 程式可以通過編譯和正常執行。過程與前相同。

    從中得到一個重要結論: 在已定義了相應的轉換建構函式情況下,將運算子“+”函式過載為友元函式,在進行兩個複數相加時,可以用交換律。

    如果運算子函式過載為成員函式,它的第一個引數必須是本類的物件。當第一個運算元不是類物件時,不能將運算子函式過載為成員函式。如果將運算子“+”函式過載為類的成員函式,交換律不適用。
    由於這個原因,一般情況下將雙目運算子函式過載為友元函式。單目運算子則多過載為成員函式。
  4. 如果一定要將運算子函式過載為成員函式,而第一個運算元又不是類物件時,只有一個辦法能夠解決,再過載一個運算子“+”函式,其第一個引數為double型。當然此函式只能是友元函式,函式原型為
       friend operator+(double,Complex &);
    顯然這樣做不太方便,還是將雙目運算子函式過載為友元函式方便些。
  5. 在上面程式的基礎上增加型別轉換函式:
       operator double( ){return real;}
    此時Complex類的公用部分為
       public:
       Complex( ){real=0;imag=0;}
       Complex(double r){real=r;imag=0;}  //轉換建構函式
       Complex(double r,double i){real=r;imag=i;}
       operator double( ){return real;}//型別轉換函式
       friend Complex operator+ (Complex c1,Complex c2); //過載運算子“+”
       void display( );
    其餘部分不變。程式在編譯時出錯,原因是出現二義性。