1. 程式人生 > >c++類的隱式轉換與強制轉換過載詳解

c++類的隱式轉換與強制轉換過載詳解

在寫這篇文章之前,讓我們先回顧一下編譯器通過匹配過程確定呼叫哪一個函式的匹配順序:
(1)尋找和使用最符合函式名和引數型別(包括返回值)的函式,若找到則呼叫;

(2)否則,尋找一個函式模板,將其例項化產生一個匹配的過載函式,若找到則呼叫;

(3)否則,尋找可以通過型別轉換進行引數匹配的過載函式,若找到則呼叫它。

如果以上步驟均未找到匹配函式,則這個呼叫是錯誤的;如果這個呼叫有多於一個的匹配選譯,則呼叫匹配出現二義性,也是錯誤的。 
  
型別轉換是將一種型別的值對映為另一種型別的值。型別轉換實際上包含有自動隱含和強制的兩種。

C語言編譯系統提供的內部資料型別的自動隱式轉換規則如下:

1.程式在執行算術運算時,低型別可以轉換為高型別。

2.在賦值表示式中,右邊表示式的值自動隱式轉換為左邊變數的型別,並賦值給它。

3.當在函式呼叫時,將實參值賦給形參,系統隱式地將實參轉換為形參的型別後,賦給形參。

4.函式有返回值時,系統將自動地將返回表示式型別轉換為函式型別後,賦值給呼叫函式。

在以上情況下,系統會進行隱式轉換的。當在程式中發現兩個資料型別不相容時,又不能自動完成隱式轉換,則將出現編譯錯誤。例如:
   int* p = 100; 
在這種情況下,編譯程式將報錯,為了消除錯誤,可以進行如下所示的強制型別轉換:
   int* p = (int *)100; 
將整型數100顯式地轉換成指標型別。

建構函式具有型別轉換功能

在實際應用中,當類定義中提供了單個引數的建構函式時,該類便提供了一種將其他資料型別的數值或變數轉換為使用者所定義資料型別的方法。因此,可以說單個引數的建構函式提供了資料轉換的功能。下面通過一個例子進一步說明單引數建構函式的型別轉換功能。

複製程式碼程式碼如下:
#include
classA 
{
public:
A(){ m=0; } 
A(doublei) { m=i; } 
voidprint() { cout<<M< 
private:
doublem; 
};

voidmain() 
{
Aa(5); 
a=10; //a與10是不同的資料型別
a.print();
}

程式的輸出結果為:

  10
在該程式中,賦值語句a=10;中,賦值號兩邊數值10和物件a是兩上不相容的資料型別,可是它卻能順利通過編譯程式,並且輸出顯示正確結果,其主要原因是得益於單引數的建構函式。編譯系統選通過標準資料型別轉換,將整型數值10轉換成double型,然後,再通過類中定義的單引數建構函式將double型數值轉換為A類型別,最後把它賦值給a。這些轉換都是自動隱式完成的。

關於上面的程式,補充一點:
Aa = 10; 

Aa; 
a= 10; 
兩者是不同的,前者對a進行初使化,編譯器會嘗試將10隱式轉換為A型別,這樣將引起a的A(doublei)建構函式直接被呼叫。
後者屬於賦值語句,編譯器將建立一個臨時物件,並將10隱式轉換為A型別。如果我們顯示呼叫

(A)10;
這也將建立一個臨時物件,引起A的建構函式被呼叫。

還有一點要注意,編譯器只會進行一次隱式轉換(C時刻庫的內建型別如intshort char等)除外,下面的語句說明了這點:
m_rst->GetFields()->GetItem(nCol)->Value= (_bstr_t)sValue; 
上面Value是COM的變體型別,“Value=”將引起operator= (_bstr_t)被呼叫。如果上面省略(_bstr_t),編譯器將發生錯誤,因為沒有operator= (char*)這樣的過載,編譯器不會為我們進行兩次以上的隱式轉換。 

在函式呼叫過程中,運算子過載和構造也是一個函式呼叫,如果匹配的函式如無二義性,那麼將可以產生一次隱式轉換。如果上句的Value變體類只有一個operate= (_bstr_t),那麼既使這樣寫->Value= sValue; 編譯器也會試圖將sValue隱式轉換為_bstr_t型別。

還有一種情況
複製程式碼程式碼如下:
classA 
{
inta; 
public:
A(){ }; 
A(int_a) { a = _a; }; 
Operatorint() { return a; } 
}

有如下呼叫:
複製程式碼程式碼如下:
Aa(10); 
Aa2 = (int)(int)a;   //只相當於Aa2 = (int)a; 因為第一個就近已經轉成了int,第二//個就不用再轉了

比較有意思吧,A類既有將int隱式轉換A的構造,也有int()轉換函式供強制轉換,(int)(int)a將以就近原則的方式進行。如果就近轉換失敗,編譯器將報錯。比如: 
複製程式碼程式碼如下:
classB 
{
};
Aa2 = (B)a; 

Aa2 = (B)10; 

編譯器報這樣的錯誤:"errorC2440: “型別轉換”:無法從“int”轉換為“B”"
可知,我們自己編寫的構造和轉換函式多麼重要。

轉換函式
轉換函式又稱型別強制轉換成員函式,它是類中的一個非靜態成員函式。它的定義格式如下:
複製程式碼程式碼如下:
   class<型別說明符1>
    {
     public:
      operator<型別說明符2>();
      …
    }

這個轉換函式定義了由<型別說明符1>到<型別說明符2>之間的對映關係。可見,轉換函式是用來將一種型別的資料轉換成為另一種型別。下面通過一個例子說明轉換函式的功能。
複製程式碼程式碼如下:
#include

classRational 
{
public:
Rational(intd, int n) 
{
den= d; 
num= n; 
}
operatordouble();//型別轉換函式
private:
intden, num; 
};

Rational::operatordouble() 
{
returndouble(den)/double(num); 
}

voidmain() 
{
Rationalr(5, 8); 
doubled = 4.7; 
d+= r;  //這句將呼叫隱式轉換,相當於d= (double)r; 
cout<<d<<ENDL;
}

程式輸出結果:
5.325

由程式可知,d是一個double型數值,r是Rational類的物件,這兩個不同型別的資料進行加法之所以能夠進行是得益於轉換函式operatordouble()。為使上述加法能夠進行,編譯系統先檢查類Rational的說明,看是否存在在下轉換函式能夠將Rational型別的運算元轉換為double型別的運算元。由於Rational類中說明了轉換函式operatordouble(),它可以在程式執行時進行上述型別轉換,因此,該程式中實現了d=r;的操作。

定義轉換函式時應注意如下幾點:
(1)轉換函式是使用者定義的成員函式,但它要是非靜態的。
(2)轉換函式的不可以有返回值。(意思是宣告中不可以有返回值)
(3)轉換函式也不帶任何引數。
(4)轉換函式函式還不能定義為友元函式。

轉換函式的名稱是型別轉換的目標型別,因此,不必再為它指定返回值型別;轉換函式是被用於本型別的數值或變數轉換為其他的型別,也不必帶引數。

類中的建構函式完成其他型別到類型別的轉換,而過載強制轉換完成類型別到其他型別的轉換。