深入講解C++數(shù)據(jù)類型轉(zhuǎn)換的相關(guān)函數(shù)的知識
C++數(shù)據(jù)類型轉(zhuǎn)換以及轉(zhuǎn)換構(gòu)造函數(shù)
標(biāo)準(zhǔn)數(shù)據(jù)類型之間的轉(zhuǎn)換
在C++中,某些不同類型數(shù)據(jù)之間可以自動(dòng)轉(zhuǎn)換,例如
int i = 6; i = 7.5 + i;
編譯系統(tǒng)對 7.5是作為double型數(shù)處理的,在求解表達(dá)式時(shí),先將6轉(zhuǎn)換成double型,然后與7.5相加,得到和為13.5,在向整型變量i賦值時(shí),將13.5轉(zhuǎn)換為整數(shù)13,然后賦給i。這種轉(zhuǎn)換是由C++編譯系統(tǒng)自動(dòng)完成的,用戶不需干預(yù)。這種轉(zhuǎn)換稱為隱式類型轉(zhuǎn)換。
C++還提供顯式類型轉(zhuǎn)換,程序人員在程序中指定將一種指定的數(shù)據(jù)轉(zhuǎn)換成另一指定的類型,其形式為:
類型名(數(shù)據(jù))
如
int(89.5)
其作用是將89.5轉(zhuǎn)換為整型數(shù)89。
以前我們接觸的是標(biāo)準(zhǔn)類型之間的轉(zhuǎn)換,現(xiàn)在用戶自己定義了類,就提出了一個(gè)問題:一個(gè)自定義類的對象能否轉(zhuǎn)換成標(biāo)準(zhǔn)類型? 一個(gè)類的對象能否轉(zhuǎn)換成另外一個(gè)類的對象?譬如,能否將一個(gè)復(fù)數(shù)類數(shù)據(jù)轉(zhuǎn)換成整數(shù)或雙精度數(shù)?能否將Date類的對象轉(zhuǎn)換成Time類的對象?
對于標(biāo)準(zhǔn)類型的轉(zhuǎn)換,編譯系統(tǒng)有章可循,知道怎樣進(jìn)行轉(zhuǎn)換。而對于用戶自己聲明的類型,編譯系統(tǒng)并不知道怎樣進(jìn)行轉(zhuǎn)換。解決這個(gè)問題的關(guān)鍵是讓編譯系統(tǒng)知道怎樣去進(jìn)行這些轉(zhuǎn)換,需要定義專門的函數(shù)來處理。
轉(zhuǎn)換構(gòu)造函數(shù)
轉(zhuǎn)換構(gòu)造函數(shù)(conversion constructor function) 的作用是將一個(gè)其他類型的數(shù)據(jù)轉(zhuǎn)換成一個(gè)類的對象。這里回顧一下以前學(xué)習(xí)過的幾種構(gòu)造函數(shù):
1) 默認(rèn)構(gòu)造函數(shù)。以Complex類為例,函數(shù)原型的形式為:
Complex( ); //沒有參數(shù)
2) 用于初始化的構(gòu)造函數(shù)。函數(shù)原型的形式為:
Complex(double r, double i); //形參表列中一般有兩個(gè)以上參數(shù)
3) 用于復(fù)制對象的復(fù)制構(gòu)造函數(shù)。函數(shù)原型的形式為:
Complex (Complex &c); //形參是本類對象的引用
現(xiàn)在介紹一種新的構(gòu)造函數(shù)——轉(zhuǎn)換構(gòu)造函數(shù)。
轉(zhuǎn)換構(gòu)造函數(shù)只有一個(gè)形參,如
Complex(double r) {real=r;imag=0;}
其作用是將double型的參數(shù)r轉(zhuǎn)換成Complex類的對象,將r作為復(fù)數(shù)的實(shí)部,虛部為0。用戶可以根據(jù)需要定義轉(zhuǎn)換構(gòu)造函數(shù),在函數(shù)體中告訴編譯系統(tǒng)怎樣去進(jìn)行轉(zhuǎn)換。
在類體中,可以有轉(zhuǎn)換構(gòu)造函數(shù),也可以沒有轉(zhuǎn)換構(gòu)造函數(shù),視需要而定。以上幾種構(gòu)造函數(shù)可以同時(shí)出現(xiàn)在同一個(gè)類中,它們是構(gòu)造函數(shù)的重載。編譯系統(tǒng)會根據(jù)建立對象時(shí)給出的實(shí)參的個(gè)數(shù)與類型選擇形參與之匹配的構(gòu)造函數(shù)。
假如在Complex類中定義了上面的構(gòu)造函數(shù),在Complex類的作用域中有以下聲明語句:
Complex cl(3.5) ; //建立對象cl,由于只有一個(gè)參數(shù),調(diào)用轉(zhuǎn)換構(gòu)造函數(shù)
建立Comptex類對象cl,其real(實(shí)部)的值為3.5,imag(虛部)的值為0。它的作用就是將double型常數(shù)轉(zhuǎn)換成一個(gè)名為cl的Complex類對象。也可以用聲明語句建立一 個(gè)無名的Complex類對象。如
Complex(3.6) ; //用聲明語句建立一個(gè)無名的對象,合法,但無法使用它
可以在一個(gè)表達(dá)式中使用無名對象,如:
cl =Complex(3.6); //假設(shè)cl巳被定義為Complex類對象
建立一個(gè)無名的Complex類對象,其值為(3.6+0i),然后將此無名對象的值賦給cl,cl 在賦值后的值是(3.6+0i)。
如果已對運(yùn)算符“+”進(jìn)行了重載,使之能進(jìn)行兩個(gè)Complex類對象的相加,若在程序中有以下表達(dá)式:
c = cl +2.5;
編譯出錯(cuò),因?yàn)椴荒苡眠\(yùn)算符“+”將一個(gè)Comptex類對象和一個(gè)浮點(diǎn)數(shù)相加??梢韵葘?2.5轉(zhuǎn)換為Complex類無名對象,然后相加:
c = cl + Complex (2.5); //合法
請對比Complex(2.5)和int(2.5)。二者形式類似,int(2.5)是強(qiáng)制類型轉(zhuǎn)換,將2.5轉(zhuǎn)換為整數(shù),int()是強(qiáng)制類型轉(zhuǎn)換運(yùn)算符??梢哉J(rèn)為Complex(2.5)的作用也是強(qiáng)制類型 轉(zhuǎn)換,將2.5轉(zhuǎn)換為Complex類對象。
轉(zhuǎn)換構(gòu)造函數(shù)也是一種構(gòu)造函數(shù),它遵循構(gòu)造函數(shù)的一般規(guī)則。通常把有一個(gè)參數(shù)的構(gòu)造函數(shù)用作類型轉(zhuǎn)換,所以,稱為轉(zhuǎn)換構(gòu)造函數(shù)。其實(shí),有一個(gè)參數(shù)的構(gòu)造函數(shù)也可以不用作類型轉(zhuǎn)換,如
Complex (double r){ cout<<r; } //這種用法毫無意義,沒有人會這樣用
轉(zhuǎn)換構(gòu)造函數(shù)的函數(shù)體是根據(jù)需要由用戶確定的,務(wù)必使其有實(shí)際意義。例如也可 以這樣定義轉(zhuǎn)換構(gòu)造函數(shù):
Complex(double r){ real =0; imag = r; }
即實(shí)部為0,虛部為r。這并不違反語法,但沒有人會這樣做。應(yīng)該符合習(xí)慣,合乎情理。
注意:轉(zhuǎn)換構(gòu)造函數(shù)只能有一個(gè)參數(shù)。如果有多個(gè)參數(shù),就不是轉(zhuǎn)換構(gòu)造函數(shù)。原因是顯然的,如果有多個(gè)參數(shù)的話,究竟是把哪個(gè)參數(shù)轉(zhuǎn)換成Complex類的對象呢?
歸納起來,使用轉(zhuǎn)換構(gòu)造函數(shù)將一個(gè)指定的數(shù)據(jù)轉(zhuǎn)換為類對象的方法如下:
1) 先聲明一個(gè)類。
2) 在這個(gè)類中定義一個(gè)只有一個(gè)參數(shù)的構(gòu)造函數(shù),參數(shù)的類型是需要轉(zhuǎn)換的類型,在函數(shù)體中指定轉(zhuǎn)換的方法。
3) 在該類的作用域內(nèi)可以用以下形式進(jìn)行類型轉(zhuǎn)換:
類名(指定類型的數(shù)據(jù))
就可以將指定類型的數(shù)據(jù)轉(zhuǎn)換為此類的對象。
不僅可以將一個(gè)標(biāo)準(zhǔn)類型數(shù)據(jù)轉(zhuǎn)換成類對象,也可以將另一個(gè)類的對象轉(zhuǎn)換成轉(zhuǎn)換構(gòu)造函數(shù)所在的類對象。如可以將一個(gè)學(xué)生類對象轉(zhuǎn)換為教師類對象,可以在Teacher類中寫出下面的轉(zhuǎn)換構(gòu)造函數(shù):
Teacher(Student& s){ num=s.num;strcpy(name, s.name);sex=s.sex; }
但應(yīng)注意,對象s中的num,name,sex必須是公用成員,否則不能被類外引用。
C++類型轉(zhuǎn)換函數(shù)(類型轉(zhuǎn)換運(yùn)算符函數(shù))
用轉(zhuǎn)換構(gòu)造函數(shù)可以將一個(gè)指定類型的數(shù)據(jù)轉(zhuǎn)換為類的對象。但是不能反過來將一個(gè)類的對象轉(zhuǎn)換為一個(gè)其他類型的數(shù)據(jù)(例如將一個(gè)Complex類對象轉(zhuǎn)換成double類型數(shù)據(jù))。
C++提供類型轉(zhuǎn)換函數(shù)(type conversion function)來解決這個(gè)問題。類型轉(zhuǎn)換函數(shù)的作用是將一個(gè)類的對象轉(zhuǎn)換成另一類型的數(shù)據(jù)。如果已聲明了一個(gè)Complex類,可以在Complex類中這樣定義類型轉(zhuǎn)換函數(shù):
operator double( ) { return real; }
函數(shù)返回double型變量real的值。它的作用是將一個(gè)Complex類對象轉(zhuǎn)換為一個(gè)double型數(shù)據(jù),其值是Complex類中的數(shù)據(jù)成員real的值。請注意,函數(shù)名是operator double,這點(diǎn)是和運(yùn)算符重載時(shí)的規(guī)律一致的(在定義運(yùn)算符“+”的重載函數(shù)時(shí),函數(shù)名是operator +)。
類型轉(zhuǎn)換函數(shù)的一般形式為:
operator 類型名( ) { 實(shí)現(xiàn)轉(zhuǎn)換的語句 }
在函數(shù)名前面不能指定函數(shù)類型,函數(shù)沒有參數(shù)。其返回值的類型是由函數(shù)名中指定的類型名來確定的。類型轉(zhuǎn)換函數(shù)只能作為成員函數(shù),因?yàn)檗D(zhuǎn)換的主體是本類的對象。不能作為友元函數(shù)或普通函數(shù)。
從函數(shù)形式可以看到,它與運(yùn)算符重載函數(shù)相似,都是用關(guān)鍵字operator開頭,只是被重載的是類型名。double類型經(jīng)過重載后,除了原有的含義外,還獲得新的含義(將一個(gè)Complex類對象轉(zhuǎn)換為double類型數(shù)據(jù),并指定了轉(zhuǎn)換方法)。這樣,編譯系統(tǒng)不僅能識別原有的double型數(shù)據(jù),而且還會把Complex類對象作為double型數(shù)據(jù)處理。
那么程序中的Complex類對具有雙重身份,既是Complex類對象,又可作為double類型數(shù)據(jù)。Complex類對象只有在需要時(shí)才進(jìn)行轉(zhuǎn)換,要根據(jù)表達(dá)式的上下文來決定。轉(zhuǎn)換構(gòu)造函數(shù)和類型轉(zhuǎn)換運(yùn)算符有一個(gè)共同的功能:當(dāng)需要的時(shí)候,編譯系統(tǒng)會自動(dòng)調(diào)用這些函數(shù),建立一個(gè)無名的臨時(shí)對象(或臨時(shí)變量)。
[例] 使用類型轉(zhuǎn)換函數(shù)的簡單例子。
#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;} //類型轉(zhuǎn)換函數(shù) private: double real; double imag; }; int main( ) { Complex c1(3,4),c2(5,-10),c3; double d; d=2.5+c1;//要求將一個(gè)double數(shù)據(jù)與Complex類數(shù)據(jù)相加 cout<<d<<endl; return 0; }
對程序的分析:
1) 如果在Complex類中沒有定義類型轉(zhuǎn)換函數(shù)operator double,程序編譯將出錯(cuò)。因?yàn)椴荒軐?shí)現(xiàn)double 型數(shù)據(jù)與Complex類對象的相加?,F(xiàn)在,已定義了成員函數(shù) operator double,就可以利用它將Complex類對象轉(zhuǎn)換為double型數(shù)據(jù)。請注意,程序中不必顯式地調(diào)用類型轉(zhuǎn)換函數(shù),它是自動(dòng)被調(diào)用的,即隱式調(diào)用。在什么情況下調(diào)用類型轉(zhuǎn)換函數(shù)呢?編譯系統(tǒng)在處理表達(dá)式 2.5 +cl 時(shí),發(fā)現(xiàn)運(yùn)算符“+”的左側(cè)是double型數(shù)據(jù),而右側(cè)是Complex類對象,又無運(yùn)算符“+”重載函數(shù),不能直接相加,編譯系統(tǒng)發(fā)現(xiàn)有對double的重載函數(shù),因此調(diào)用這個(gè)函數(shù),返回一個(gè)double型數(shù)據(jù),然后與2.5相加。
2) 如果在main函數(shù)中加一個(gè)語句:
c3=c2;
請問此時(shí)編譯系統(tǒng)是把c2按Complex類對象處理呢,還是按double型數(shù)據(jù)處理?由于賦值號兩側(cè)都是同一類的數(shù)據(jù),是可以合法進(jìn)行賦值的,沒有必要把c2轉(zhuǎn)換為double型數(shù)據(jù)。
3) 如果在Complex類中聲明了重載運(yùn)算符“+”函數(shù)作為友元函數(shù):
Complex operator+ (Complex c1,Complex c2)//定義運(yùn)算符“+”重載函數(shù) { return Complex(c1.real+c2.real, c1.imag+c2.imag); }
若在main函數(shù)中有語句
c3=c1+c2;
由于已對運(yùn)算符“+”重載,使之能用于兩個(gè)Complex類對象的相加,因此將c1和c2按Complex類對象處理,相加后賦值給同類對象c3。如果改為
d=c1+c2; //d為double型變量
將c1與c2兩個(gè)類對象相加,得到一個(gè)臨時(shí)的Complex類對象,由于它不能賦值給double型變量,而又有對double的重載函數(shù),于是調(diào)用此函數(shù),把臨時(shí)類對象轉(zhuǎn)換為double數(shù)據(jù),然后賦給d。
從前面的介紹可知,對類型的重載和對運(yùn)算符的重載的概念和方法都是相似的,重載函數(shù)都使用關(guān)鍵字operator。因此,通常把類型轉(zhuǎn)換函數(shù)也稱為類型轉(zhuǎn)換運(yùn)算符函數(shù),由于它也是重載函數(shù),因此也稱為類型轉(zhuǎn)換運(yùn)算符重載函數(shù)(或稱強(qiáng)制類型轉(zhuǎn)換運(yùn)算符重載函數(shù))。
假如程序中需要對一個(gè)Complex類對象和一個(gè)double型變量進(jìn)行+,-,*,/等算術(shù)運(yùn)算,以及關(guān)系運(yùn)算和邏輯運(yùn)算,如果不用類型轉(zhuǎn)換函數(shù),就要對多種運(yùn)算符進(jìn)行重載,以便能進(jìn)行各種運(yùn)算。這樣,是十分麻煩的,工作量較大,程序顯得冗長。如果用類型轉(zhuǎn)換函數(shù)對double進(jìn)行重載(使Complex類對象轉(zhuǎn)換為double型數(shù)據(jù)),就不必對各種運(yùn)算符進(jìn)行重載,因?yàn)镃omplex類對象可以被自動(dòng)地轉(zhuǎn)換為double型數(shù)據(jù),而標(biāo)準(zhǔn)類型的數(shù)據(jù)的運(yùn)算,是可以使用系統(tǒng)提供的各種運(yùn)算符的。
[例] 包含轉(zhuǎn)換構(gòu)造函數(shù)、運(yùn)算符重載函數(shù)和類型轉(zhuǎn)換函數(shù)的程序。先閱讀以下程序,在這個(gè)程序中只包含轉(zhuǎn)換構(gòu)造函數(shù)和運(yùn)算符重載函數(shù)。
#include <iostream> using namespace std; class Complex { public: Complex( ){real=0;imag=0;} //默認(rèn)構(gòu)造函數(shù) Complex(double r){real=r;imag=0;}//轉(zhuǎn)換構(gòu)造函數(shù) Complex(double r,double i){real=r;imag=i;}//實(shí)現(xiàn)初始化的構(gòu)造函數(shù) friend Complex operator + (Complex c1,Complex c2); //重載運(yùn)算符“+”的友元函數(shù) void display( ); private: double real; double imag; }; Complex operator + (Complex c1,Complex c2)//定義運(yùn)算符“+”重載函數(shù) { 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; //復(fù)數(shù)與double數(shù)據(jù)相加 c3.display( ); return 0; }
注意,在Visual C++ 6.0環(huán)境下運(yùn)行時(shí),需將第一行改為#include <iostream.h>,并刪去第2行,否則編譯不能通過。
對程序的分析:
1) 如果沒有定義轉(zhuǎn)換構(gòu)造函數(shù),則此程序編譯出錯(cuò)。
2) 現(xiàn)在,在類Complex中定義了轉(zhuǎn)換構(gòu)造函數(shù),并具體規(guī)定了怎樣構(gòu)成一個(gè)復(fù)數(shù)。由于已重載了算符“+”,在處理表達(dá)式c1+2.5時(shí),編譯系統(tǒng)把它解釋為
operator+(c1, 2.5)
由于2.5不是Complex類對象,系統(tǒng)先調(diào)用轉(zhuǎn)換構(gòu)造函數(shù)Complex(2.5),建立一個(gè)臨時(shí)的Complex類對象,其值為(2.5+0i)。上面的函數(shù)調(diào)用相當(dāng)于
operator+(c1, Complex(2.5))
將c1與(2.5+0i) 相加,賦給c3。運(yùn)行結(jié)果為
(5.5+4i)
3) 如果把“c3=c1+2.5;”改為c3=2.5+c1; 程序可以通過編譯和正常運(yùn)行。過程與前相同。
從中得到一個(gè)重要結(jié)論,在已定義了相應(yīng)的轉(zhuǎn)換構(gòu)造函數(shù)情況下,將運(yùn)算符“+”函數(shù)重載為友元函數(shù),在進(jìn)行兩個(gè)復(fù)數(shù)相加時(shí),可以用交換律。
如果運(yùn)算符函數(shù)重載為成員函數(shù),它的第一個(gè)參數(shù)必須是本類的對象。當(dāng)?shù)谝粋€(gè)操作數(shù)不是類對象時(shí),不能將運(yùn)算符函數(shù)重載為成員函數(shù)。如果將運(yùn)算符“+”函數(shù)重載為類的成員函數(shù),交換律不適用。
由于這個(gè)原因,一般情況下將雙目運(yùn)算符函數(shù)重載為友元函數(shù)。單目運(yùn)算符則多重載為成員函數(shù)。
4) 如果一定要將運(yùn)算符函數(shù)重載為成員函數(shù),而第一個(gè)操作數(shù)又不是類對象時(shí),只有一個(gè)辦法能夠解決,再重載一個(gè)運(yùn)算符“+”函數(shù),其第一個(gè)參數(shù)為double型。當(dāng)然此函數(shù)只能是友元函數(shù),函數(shù)原型為
friend operator+(double, Complex &);
顯然這樣做不太方便,還是將雙目運(yùn)算符函數(shù)重載為友元函數(shù)方便些。
5) 在上面程序的基礎(chǔ)上增加類型轉(zhuǎn)換函數(shù):
operator double( ){return real;}
此時(shí)Complex類的公用部分為:
public: Complex( ){real=0;imag=0;} Complex(double r){real=r;imag=0;} //轉(zhuǎn)換構(gòu)造函數(shù) Complex(double r,double i){real=r;imag=i;} operator double( ){return real;}//類型轉(zhuǎn)換函數(shù) friend Complex operator+ (Complex c1,Complex c2); //重載運(yùn)算符“+” void display( );
其余部分不變。程序在編譯時(shí)出錯(cuò),原因是出現(xiàn)二義性。
相關(guān)文章
利用C語言實(shí)現(xiàn)經(jīng)典多級時(shí)間輪定時(shí)器
C語言是一門通用計(jì)算機(jī)編程語言,廣泛應(yīng)用于底層開發(fā),這篇文章主要給大家介紹了關(guān)于利用C語言實(shí)現(xiàn)經(jīng)典多級時(shí)間輪定時(shí)器的相關(guān)資料,需要的朋友可以參考下2021-07-07linux下C/C++學(xué)生信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了linux下c/c++學(xué)生信息管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01C語言拓展實(shí)現(xiàn)Lua sleep函數(shù)
這篇文章主要介紹了C語言拓展實(shí)現(xiàn)Lua sleep函數(shù),本文使用C語言寫出sleep函數(shù),編譯后在Lua中調(diào)用,需要的朋友可以參考下2015-04-04C語言實(shí)現(xiàn)txt數(shù)據(jù)讀入內(nèi)存/CPU緩存實(shí)例詳解
這篇文章主要介紹了C語言實(shí)現(xiàn)txt數(shù)據(jù)讀入內(nèi)存/CPU緩存實(shí)例詳解的相關(guān)資料,這里對實(shí)現(xiàn)該函數(shù)進(jìn)行了代碼實(shí)現(xiàn),需要的朋友可以參考下2017-01-01c++中為什么可以通過指針或引用實(shí)現(xiàn)多態(tài)詳解
這篇文章主要給大家介紹了關(guān)于c++中為何可以通過指針或引用實(shí)現(xiàn)多態(tài),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04深入Windows下的回車是回車換行(\r\n)還是換行回車(\n\r)的詳解
本篇文章對Windows下的回車是回車換行(\r\n)還是換行回車(\n\r)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05