深入講解C++數據類型轉換的相關函數的知識
C++數據類型轉換以及轉換構造函數
標準數據類型之間的轉換
在C++中,某些不同類型數據之間可以自動轉換,例如
int i = 6; i = 7.5 + i;
編譯系統(tǒng)對 7.5是作為double型數處理的,在求解表達式時,先將6轉換成double型,然后與7.5相加,得到和為13.5,在向整型變量i賦值時,將13.5轉換為整數13,然后賦給i。這種轉換是由C++編譯系統(tǒng)自動完成的,用戶不需干預。這種轉換稱為隱式類型轉換。
C++還提供顯式類型轉換,程序人員在程序中指定將一種指定的數據轉換成另一指定的類型,其形式為:
類型名(數據)
如
int(89.5)
其作用是將89.5轉換為整型數89。
以前我們接觸的是標準類型之間的轉換,現在用戶自己定義了類,就提出了一個問題:一個自定義類的對象能否轉換成標準類型? 一個類的對象能否轉換成另外一個類的對象?譬如,能否將一個復數類數據轉換成整數或雙精度數?能否將Date類的對象轉換成Time類的對象?
對于標準類型的轉換,編譯系統(tǒng)有章可循,知道怎樣進行轉換。而對于用戶自己聲明的類型,編譯系統(tǒng)并不知道怎樣進行轉換。解決這個問題的關鍵是讓編譯系統(tǒng)知道怎樣去進行這些轉換,需要定義專門的函數來處理。
轉換構造函數
轉換構造函數(conversion constructor function) 的作用是將一個其他類型的數據轉換成一個類的對象。這里回顧一下以前學習過的幾種構造函數:
1) 默認構造函數。以Complex類為例,函數原型的形式為:
Complex( ); //沒有參數
2) 用于初始化的構造函數。函數原型的形式為:
Complex(double r, double i); //形參表列中一般有兩個以上參數
3) 用于復制對象的復制構造函數。函數原型的形式為:
Complex (Complex &c); //形參是本類對象的引用
現在介紹一種新的構造函數——轉換構造函數。
轉換構造函數只有一個形參,如
Complex(double r) {real=r;imag=0;}
其作用是將double型的參數r轉換成Complex類的對象,將r作為復數的實部,虛部為0。用戶可以根據需要定義轉換構造函數,在函數體中告訴編譯系統(tǒng)怎樣去進行轉換。
在類體中,可以有轉換構造函數,也可以沒有轉換構造函數,視需要而定。以上幾種構造函數可以同時出現在同一個類中,它們是構造函數的重載。編譯系統(tǒng)會根據建立對象時給出的實參的個數與類型選擇形參與之匹配的構造函數。
假如在Complex類中定義了上面的構造函數,在Complex類的作用域中有以下聲明語句:
Complex cl(3.5) ; //建立對象cl,由于只有一個參數,調用轉換構造函數
建立Comptex類對象cl,其real(實部)的值為3.5,imag(虛部)的值為0。它的作用就是將double型常數轉換成一個名為cl的Complex類對象。也可以用聲明語句建立一 個無名的Complex類對象。如
Complex(3.6) ; //用聲明語句建立一個無名的對象,合法,但無法使用它
可以在一個表達式中使用無名對象,如:
cl =Complex(3.6); //假設cl巳被定義為Complex類對象
建立一個無名的Complex類對象,其值為(3.6+0i),然后將此無名對象的值賦給cl,cl 在賦值后的值是(3.6+0i)。
如果已對運算符“+”進行了重載,使之能進行兩個Complex類對象的相加,若在程序中有以下表達式:
c = cl +2.5;
編譯出錯,因為不能用運算符“+”將一個Comptex類對象和一個浮點數相加??梢韵葘?2.5轉換為Complex類無名對象,然后相加:
c = cl + Complex (2.5); //合法
請對比Complex(2.5)和int(2.5)。二者形式類似,int(2.5)是強制類型轉換,將2.5轉換為整數,int()是強制類型轉換運算符。可以認為Complex(2.5)的作用也是強制類型 轉換,將2.5轉換為Complex類對象。
轉換構造函數也是一種構造函數,它遵循構造函數的一般規(guī)則。通常把有一個參數的構造函數用作類型轉換,所以,稱為轉換構造函數。其實,有一個參數的構造函數也可以不用作類型轉換,如
Complex (double r){ cout<<r; } //這種用法毫無意義,沒有人會這樣用
轉換構造函數的函數體是根據需要由用戶確定的,務必使其有實際意義。例如也可 以這樣定義轉換構造函數:
Complex(double r){ real =0; imag = r; }
即實部為0,虛部為r。這并不違反語法,但沒有人會這樣做。應該符合習慣,合乎情理。
注意:轉換構造函數只能有一個參數。如果有多個參數,就不是轉換構造函數。原因是顯然的,如果有多個參數的話,究竟是把哪個參數轉換成Complex類的對象呢?
歸納起來,使用轉換構造函數將一個指定的數據轉換為類對象的方法如下:
1) 先聲明一個類。
2) 在這個類中定義一個只有一個參數的構造函數,參數的類型是需要轉換的類型,在函數體中指定轉換的方法。
3) 在該類的作用域內可以用以下形式進行類型轉換:
類名(指定類型的數據)
就可以將指定類型的數據轉換為此類的對象。
不僅可以將一個標準類型數據轉換成類對象,也可以將另一個類的對象轉換成轉換構造函數所在的類對象。如可以將一個學生類對象轉換為教師類對象,可以在Teacher類中寫出下面的轉換構造函數:
Teacher(Student& s){ num=s.num;strcpy(name, s.name);sex=s.sex; }
但應注意,對象s中的num,name,sex必須是公用成員,否則不能被類外引用。
C++類型轉換函數(類型轉換運算符函數)
用轉換構造函數可以將一個指定類型的數據轉換為類的對象。但是不能反過來將一個類的對象轉換為一個其他類型的數據(例如將一個Complex類對象轉換成double類型數據)。
C++提供類型轉換函數(type conversion function)來解決這個問題。類型轉換函數的作用是將一個類的對象轉換成另一類型的數據。如果已聲明了一個Complex類,可以在Complex類中這樣定義類型轉換函數:
operator double( )
{
return real;
}
函數返回double型變量real的值。它的作用是將一個Complex類對象轉換為一個double型數據,其值是Complex類中的數據成員real的值。請注意,函數名是operator double,這點是和運算符重載時的規(guī)律一致的(在定義運算符“+”的重載函數時,函數名是operator +)。
類型轉換函數的一般形式為:
operator 類型名( )
{
實現轉換的語句
}
在函數名前面不能指定函數類型,函數沒有參數。其返回值的類型是由函數名中指定的類型名來確定的。類型轉換函數只能作為成員函數,因為轉換的主體是本類的對象。不能作為友元函數或普通函數。
從函數形式可以看到,它與運算符重載函數相似,都是用關鍵字operator開頭,只是被重載的是類型名。double類型經過重載后,除了原有的含義外,還獲得新的含義(將一個Complex類對象轉換為double類型數據,并指定了轉換方法)。這樣,編譯系統(tǒng)不僅能識別原有的double型數據,而且還會把Complex類對象作為double型數據處理。
那么程序中的Complex類對具有雙重身份,既是Complex類對象,又可作為double類型數據。Complex類對象只有在需要時才進行轉換,要根據表達式的上下文來決定。轉換構造函數和類型轉換運算符有一個共同的功能:當需要的時候,編譯系統(tǒng)會自動調用這些函數,建立一個無名的臨時對象(或臨時變量)。
[例] 使用類型轉換函數的簡單例子。
#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,程序編譯將出錯。因為不能實現double 型數據與Complex類對象的相加?,F在,已定義了成員函數 operator double,就可以利用它將Complex類對象轉換為double型數據。請注意,程序中不必顯式地調用類型轉換函數,它是自動被調用的,即隱式調用。在什么情況下調用類型轉換函數呢?編譯系統(tǒng)在處理表達式 2.5 +cl 時,發(fā)現運算符“+”的左側是double型數據,而右側是Complex類對象,又無運算符“+”重載函數,不能直接相加,編譯系統(tǒng)發(fā)現有對double的重載函數,因此調用這個函數,返回一個double型數據,然后與2.5相加。
2) 如果在main函數中加一個語句:
c3=c2;
請問此時編譯系統(tǒng)是把c2按Complex類對象處理呢,還是按double型數據處理?由于賦值號兩側都是同一類的數據,是可以合法進行賦值的,沒有必要把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型數據,而標準類型的數據的運算,是可以使用系統(tǒng)提供的各種運算符的。
[例] 包含轉換構造函數、運算符重載函數和類型轉換函數的程序。先閱讀以下程序,在這個程序中只包含轉換構造函數和運算符重載函數。
#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;
}
注意,在Visual C++ 6.0環(huán)境下運行時,需將第一行改為#include <iostream.h>,并刪去第2行,否則編譯不能通過。
對程序的分析:
1) 如果沒有定義轉換構造函數,則此程序編譯出錯。
2) 現在,在類Complex中定義了轉換構造函數,并具體規(guī)定了怎樣構成一個復數。由于已重載了算符“+”,在處理表達式c1+2.5時,編譯系統(tǒng)把它解釋為
operator+(c1, 2.5)
由于2.5不是Complex類對象,系統(tǒng)先調用轉換構造函數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( );
其余部分不變。程序在編譯時出錯,原因是出現二義性。

