一文詳解C++中的轉(zhuǎn)換構(gòu)造函數(shù)
在 C/C++ 中,不同的數(shù)據(jù)類型之間可以相互轉(zhuǎn)換。無需用戶指明如何轉(zhuǎn)換的稱為自動類型轉(zhuǎn)換(隱式類型轉(zhuǎn)換),需要用戶顯式地指明如何轉(zhuǎn)換的稱為強制類型轉(zhuǎn)換。
自動類型轉(zhuǎn)換示例:
int a = 6; a = 7.5 + a;
編譯器對 7.5 是作為 double 類型處理的,在求解表達式時,先將 a 轉(zhuǎn)換為 double 類型,然后與 7.5 相加,得到和為 13.5。在向整型變量 a 賦值時,將 13.5 轉(zhuǎn)換為整數(shù) 13,然后賦給 a。整個過程中,我們并沒有告訴編譯器如何去做,編譯器使用內(nèi)置的規(guī)則完成數(shù)據(jù)類型的轉(zhuǎn)換。強制類型轉(zhuǎn)換示例:
int n = 100; int *p1 = &n; float *p2 = (float*)p1;
p1 是int *
類型,它指向的內(nèi)存里面保存的是整數(shù),p2 是float *
類型,將 p1 賦值給 p2 后,p2 也指向了這塊內(nèi)存,并把這塊內(nèi)存中的數(shù)據(jù)作為小數(shù)處理。我們知道,整數(shù)和小數(shù)的存儲格式大相徑庭,將整數(shù)作為小數(shù)處理非?;恼Q,可能會引發(fā)莫名其妙的錯誤,所以編譯器默認不允許將 p1 賦值給 p2。但是,使用強制類型轉(zhuǎn)換后,編譯器就認為我們知道這種風險的存在,并進行了適當?shù)臋?quán)衡,所以最終還是允許了這種行為。
不管是自動類型轉(zhuǎn)換還是強制類型轉(zhuǎn)換,前提必須是編譯器知道如何轉(zhuǎn)換,例如,將小數(shù)轉(zhuǎn)換為整數(shù)會抹掉小數(shù)點后面的數(shù)字,將int *
轉(zhuǎn)換為float *
只是簡單地復制指針的值,這些規(guī)則都是編譯器內(nèi)置的,我們并沒有告訴編譯器。換句話說,如果編譯器不知道轉(zhuǎn)換規(guī)則就不能轉(zhuǎn)換,使用強制類型也無用,請看下面的例子:
#include <iostream> using namespace std; //復數(shù)類 class Complex{ public: Complex(): m_real(0.0), m_imag(0.0){ } Complex(double real, double imag): m_real(real), m_imag(imag){ } public: friend ostream & operator<<(ostream &out, Complex &c); //友元函數(shù) private: double m_real; //實部 double m_imag; //虛部 }; //重載>>運算符 ostream & operator<<(ostream &out, Complex &c){ out << c.m_real <<" + "<< c.m_imag <<"i";; return out; } int main(){ Complex a(10.0, 20.0); a = (Complex)25.5; //錯誤,轉(zhuǎn)換失敗 return 0; }
25.5 是實數(shù),a 是復數(shù),將 25.5 賦值給 a 后,我們期望 a 的實部變?yōu)?25.5,而虛部為 0。但是,編譯器并不知道這個轉(zhuǎn)換規(guī)則,這超出了編譯器的處理能力,所以轉(zhuǎn)換失敗,即使加上強制類型轉(zhuǎn)換也無用。
幸運的是,C++ 允許我們自定義類型轉(zhuǎn)換規(guī)則,用戶可以將其它類型轉(zhuǎn)換為當前類類型,也可以將當前類類型轉(zhuǎn)換為其它類型。
這種自定義的類型轉(zhuǎn)換規(guī)則只能以類的成員函數(shù)的形式出現(xiàn),換句話說,這種轉(zhuǎn)換規(guī)則只適用于類。本節(jié)我們先講解如何將其它類型轉(zhuǎn)換為當前類類型,下節(jié)再講解如何將當前類類型轉(zhuǎn)換為其它類型。
轉(zhuǎn)換構(gòu)造函數(shù)
將其它類型轉(zhuǎn)換為當前類類型需要借助轉(zhuǎn)換構(gòu)造函數(shù)(Conversion constructor)。轉(zhuǎn)換構(gòu)造函數(shù)也是一種構(gòu)造函數(shù),它遵循構(gòu)造函數(shù)的一般規(guī)則。轉(zhuǎn)換構(gòu)造函數(shù)只有一個參數(shù)。
仍然以 Complex 類為例,我們?yōu)樗砑愚D(zhuǎn)換構(gòu)造函數(shù):
#include <iostream> using namespace std; //復數(shù)類 class Complex{ public: Complex(): m_real(0.0), m_imag(0.0){ } Complex(double real, double imag): m_real(real), m_imag(imag){ } Complex(double real): m_real(real), m_imag(0.0){ } //轉(zhuǎn)換構(gòu)造函數(shù) public: friend ostream & operator<<(ostream &out, Complex &c); //友元函數(shù) private: double m_real; //實部 double m_imag; //虛部 }; //重載>>運算符 ostream & operator<<(ostream &out, Complex &c){ out << c.m_real <<" + "<< c.m_imag <<"i";; return out; } int main(){ Complex a(10.0, 20.0); cout<<a<<endl; a = 25.5; //調(diào)用轉(zhuǎn)換構(gòu)造函數(shù) cout<<a<<endl; return 0; }
運行結(jié)果:
10 + 20i
25.5 + 0i
Complex(double real);
就是轉(zhuǎn)換構(gòu)造函數(shù),它的作用是將 double 類型的參數(shù) real 轉(zhuǎn)換成 Complex 類的對象,并將 real 作為復數(shù)的實部,將 0 作為復數(shù)的虛部。這樣一來,a = 25.5;
整體上的效果相當于:
a.Complex(25.5);
將賦值的過程轉(zhuǎn)換成了函數(shù)調(diào)用的過程。在進行數(shù)學運算、賦值、拷貝等操作時,如果遇到類型不兼容、需要將 double 類型轉(zhuǎn)換為 Complex 類型時,編譯器會檢索當前的類是否定義了轉(zhuǎn)換構(gòu)造函數(shù),如果沒有定義的話就轉(zhuǎn)換失敗,如果定義了的話就調(diào)用轉(zhuǎn)換構(gòu)造函數(shù)。
轉(zhuǎn)換構(gòu)造函數(shù)也是構(gòu)造函數(shù)的一種,它除了可以用來將其它類型轉(zhuǎn)換為當前類類型,還可以用來初始化對象,這是構(gòu)造函數(shù)本來的意義。下面創(chuàng)建對象的方式是正確的:
Complex c1(26.4); //創(chuàng)建具名對象 Complex c2 = 240.3; //以拷貝的方式初始化對象 Complex(15.9); //創(chuàng)建匿名對象 c1 = Complex(46.9); //創(chuàng)建一個匿名對象并將它賦值給 c1
在以拷貝的方式初始化對象時,編譯器先調(diào)用轉(zhuǎn)換構(gòu)造函數(shù),將 240.3 轉(zhuǎn)換為 Complex 類型(創(chuàng)建一個 Complex 類的匿名對象),然后再拷貝給 c2。如果已經(jīng)對+
運算符進行了重載,使之能進行兩個 Complex 類對象的相加,那么下面的語句也是正確的:
Complex c1(15.6, 89.9); Complex c2; c2 = c1 + 29.6; cout<<c2<<endl;
在進行加法運算符時,編譯器先將 29.6 轉(zhuǎn)換為 Complex 類型(創(chuàng)建一個 Complex 類的匿名對象)再相加。需要注意的是,為了獲得目標類型,編譯器會“不擇手段”,會綜合使用內(nèi)置的轉(zhuǎn)換規(guī)則和用戶自定義的轉(zhuǎn)換規(guī)則,并且會進行多級類型轉(zhuǎn)換,例如:
- 編譯器會根據(jù)內(nèi)置規(guī)則先將 int 轉(zhuǎn)換為 double,再根據(jù)用戶自定義規(guī)則將 double 轉(zhuǎn)換為 Complex(int --> double --> Complex);
- 編譯器會根據(jù)內(nèi)置規(guī)則先將 char 轉(zhuǎn)換為 int,再將 int 轉(zhuǎn)換為 double,最后根據(jù)用戶自定義規(guī)則將 double 轉(zhuǎn)換為 Complex(char --> int --> double --> Complex)。
從本例看,只要一個類型能轉(zhuǎn)換為 double 類型,就能轉(zhuǎn)換為 Complex 類型。請看下面的例子:
int main(){ Complex c1 = 100; //int --> double --> Complex cout<<c1<<endl; c1 = 'A'; //char --> int --> double --> Complex cout<<c1<<endl; c1 = true; //bool --> int --> double --> Complex cout<<c1<<endl; Complex c2(25.8, 0.7); //假設(shè)已經(jīng)重載了+運算符 c1 = c2 + 'H' + true + 15; //將char、bool、int都轉(zhuǎn)換為Complex類型再運算 cout<<c1<<endl; return 0; }
運行結(jié)果:
100 + 0i
65 + 0i
1 + 0i
113.8 + 0.7i
再談構(gòu)造函數(shù)
構(gòu)造函數(shù)的本意是在創(chuàng)建對象的時候初始化對象,編譯器會根據(jù)傳遞的實參來匹配不同的(重載的)構(gòu)造函數(shù)?;仡櫼幌乱郧暗恼鹿?jié),到目前為止我們已經(jīng)學習了以下幾種構(gòu)造函數(shù)。
- 默認構(gòu)造函數(shù)。就是編譯器自動生成的構(gòu)造函數(shù)。以 Complex 類為例,它的原型為:
Complex(); //沒有參數(shù)
- 普通構(gòu)造函數(shù)。就是用戶自定義的構(gòu)造函數(shù)。以 Complex 類為例,它的原型為:
Complex(double real, double imag); //兩個參數(shù)
- 拷貝構(gòu)造函數(shù)。在以拷貝的方式初始化對象時調(diào)用。以 Complex 類為例,它的原型為:
Complex(const Complex &c);
- 轉(zhuǎn)換構(gòu)造函數(shù)。將其它類型轉(zhuǎn)換為當前類類型時調(diào)用。以 Complex 為例,它的原型為:
Complex(double real);
不管哪一種構(gòu)造函數(shù),都能夠用來初始化對象,這是構(gòu)造函數(shù)的本意。假設(shè) Complex 類定義了以上所有的構(gòu)造函數(shù),那么下面創(chuàng)建對象的方式都是正確的:
Complex c1(); //調(diào)用Complex() Complex c2(10, 20); //調(diào)用Complex(double real, double imag) Complex c3(c2); //調(diào)用Complex(const Complex &c) Complex c4(25.7); //調(diào)用Complex(double real)
這些代碼都體現(xiàn)了構(gòu)造函數(shù)的本意——在創(chuàng)建對象時初始化對象。除了在創(chuàng)建對象時初始化對象,其他情況下也會調(diào)用構(gòu)造函數(shù),例如,以拷貝的的方式初始化對象時會調(diào)用拷貝構(gòu)造函數(shù),將其它類型轉(zhuǎn)換為當前類類型時會調(diào)用轉(zhuǎn)換構(gòu)造函數(shù)。這些在其他情況下調(diào)用的構(gòu)造函數(shù),就成了特殊的構(gòu)造函數(shù)了。特殊的構(gòu)造函數(shù)并不一定能體現(xiàn)出構(gòu)造函數(shù)的本意。
對 Complex 類的進一步精簡
上面的 Complex 類中我們定義了三個構(gòu)造函數(shù),其中包括兩個普通的構(gòu)造函數(shù)和一個轉(zhuǎn)換構(gòu)造函數(shù)。其實,借助函數(shù)的默認參數(shù),我們可以將這三個構(gòu)造函數(shù)簡化為一個,請看下面的代碼:
#include <iostream> using namespace std; //復數(shù)類 class Complex{ public: Complex(double real = 0.0, double imag = 0.0): m_real(real), m_imag(imag){ } public: friend ostream & operator<<(ostream &out, Complex &c); //友元函數(shù) private: double m_real; //實部 double m_imag; //虛部 }; //重載>>運算符 ostream & operator<<(ostream &out, Complex &c){ out << c.m_real <<" + "<< c.m_imag <<"i";; return out; } int main(){ Complex a(10.0, 20.0); //向構(gòu)造函數(shù)傳遞 2 個實參,不使用默認參數(shù) Complex b(89.5); //向構(gòu)造函數(shù)傳遞 1 個實參,使用 1 個默認參數(shù) Complex c; //不向構(gòu)造函數(shù)傳遞實參,使用全部默認參數(shù) a = 25.5; //調(diào)用轉(zhuǎn)換構(gòu)造函數(shù)(向構(gòu)造函數(shù)傳遞 1 個實參,使用 1 個默認參數(shù)) return 0; }
精簡后的構(gòu)造函數(shù)包含了兩個默認參數(shù),在調(diào)用它時可以省略部分或者全部實參,也就是可以向它傳遞 0 個、1 個、2 個實參。轉(zhuǎn)換構(gòu)造函數(shù)就是包含了一個參數(shù)的構(gòu)造函數(shù),恰好能夠和其他兩個普通的構(gòu)造函數(shù)“融合”在一起。
以上就是一文詳解C++中的轉(zhuǎn)換構(gòu)造函數(shù)的詳細內(nèi)容,更多關(guān)于C++轉(zhuǎn)換構(gòu)造函數(shù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語言中strcpy和strcat的使用和模擬實現(xiàn)
strcpy()?函數(shù)是?C語言中一個非常重要的字符串處理函數(shù),其功能是將一個字符串復制到另一個字符串中,strcat函數(shù)可以將一個字符串拼接到另一個字符串的末尾,本文給大家介紹了C語言中strcpy和strcat的使用和模擬實現(xiàn),需要的朋友可以參考下2024-03-03C++面向?qū)ο笾鄳B(tài)的實現(xiàn)和應用詳解
相信大家都知道面向?qū)ο蟮娜筇匦允欠庋b,繼承和多態(tài),下面這篇文章主要給大家介紹了關(guān)于C++面向?qū)ο笾鄳B(tài)的實現(xiàn)和應用的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面來一起看看吧。2017-09-09C++實現(xiàn)圖書管理系統(tǒng)課程設(shè)計
這篇文章主要為大家詳細介紹了C++實現(xiàn)圖書管理系統(tǒng)課程設(shè)計,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03