在程式設計中,型別轉換在所難免,在此我將介紹一下c++中常用的隱式轉換和強制型別轉換。
關於隱式轉換:
在c++中,某些型別之間存在相關的依賴關係,若兩種型別相關,則可以再需要某種型別的運算元位置上,使用該型別的相關型別物件或值。
c++並不是吧兩個不同型別的值直接加在一起,而提供了一組轉換規則,一邊在執行算數操作之前,將兩個運算元轉換為同一種資料型別。這些轉換規則由編譯器自動執行,無需我們再介入。因此,也被成為隱式轉換。
發生隱式轉換的情況:
1.在混合型別的表示式中,其運算元被轉換為相同型別。
2.用作條件的表示式(?:,!,&&,||,if,while,for,do while)被轉換為bool型別。
3.用一表示式初始化某個變數,或將一表示式被轉換為該變數的型別。
隱式轉換型別:
1.指標轉換:在使用陣列時,大多數情況下陣列都會自動轉換為指向第乙個元素的指標。
int ia[10];
int *ip = ia;
c++還提供了另外兩種指標轉換:指向任意資料型別的指標都可以轉換為void*型別,整型數值常量0可轉換為任意指標型別。
2.轉換為bool型別:算數值和指標值都可以轉換為bool型別。如果指標或算數值為0,則其bool值為false,而其他值則為true。
if (cp)
while (*cp)
3.算術型別與bool型別的轉換:可將bool物件轉換為int型。true變為1,false則為0。
4.轉換與列舉型別:c++自動將列舉型別的物件或列舉成員轉換為整型,其轉換結果可用於任何要求使用整數的地方。
5.轉換為const物件:當使用非const初始化const物件的引用時,系統將非const物件轉換為const物件。此外還可以將非const物件的位址轉換為指向相關const型別的指標。
int i;
const int ci = 0;
const int &j = i;
const int *p = &ci;
6.由標準庫型別定義的轉換:類型別可以定義由編譯器自動執行的型別轉換。
string s;
while (cin >> s)
其中使用了io標準庫定義的型別轉換。
關於顯示轉換:
顯示轉換也成為強制型別轉換,雖然有時候確實需要強制型別轉換,但本質非常危險,應該盡量避免使用強制型別轉換。
儘管不建議使用強制型別轉換,但還是有一些情況需要使用:
因為要覆蓋通常的標準轉換,所以需要顯示使用強制型別轉換。例如:
double dval;
int ival;
ival *= dval;
這時,需要將ival轉換為double型,然後將乘法操作的double型結截尾為int型,再賦值給ival。這樣回事的其中多了ival轉換為double型這個不必要的轉換,則需要強制型別轉換。
還有乙個原因就是:可能存在多種轉換時,需要選擇一種特定的型別轉換。
現在來討論下命名的強制型別轉換,具體格式如下:
cast-name(expression);
cast-name為命名的強制型別轉換符號,type為轉換的目標型別,expression為被強制轉換的值。
1.dynamic_cast
2.const_cast
顧名思義,就是將表示式的const屬性轉換掉。比如:
const char *pc_str;
char *pc = string_copy(const_cast(pc_str));
只用使用const_cast才能將const性質轉換掉。在這種情況下,試圖使用其他三種形式的強制轉換都會導致編譯時的錯誤。類似地,除了新增或者刪除const特性,用const_cast符來執行其他任何型別轉換,都會引起編譯錯誤。
3.static_cast
編譯器隱式執行的任何型別轉換都可以由static_cast顯式完成。當要講乙個較大的算數型別複製給較小的型別時,使用強制型別轉換非常重要。此時,相當於告訴編譯器我們忽略精度的損失,警告資訊當然也會被關閉。
當編譯器不提供自動轉換時,也可以用static_cast來轉換。
4.repinterpret_cast
通常作為運算元的位模式提供較低層次的重新解釋。(repinterpret_cast本質上依賴於機器,但為了安全的使用它,我們必須理解所涉及的資料型別,以及編譯器實現強制型別轉換的細節。)例如:
int *ip;
char *pc = reinterpret_cast(ip);
此時通過轉換後,pc實際上指向的物件是int型而非字元陣列。
(注:我們在平時程式設計中還是最好避免使用強制型別轉換,一面出現無法**的錯誤。)
說到c++中的強制型別轉換,還記得c中的強制型別轉換:
type(expr);
(type)expr;
這兩種強制型別轉換在c++中仍然支援。支援c語言中的強制型別轉換符號也是為了讓c++保持與c語言的相容性。
首先我們看下一種比較常見的技術——類建構函式的隱式轉換。這兒先說明下,之後的例子中,我會為了盡量突出主要內容,而忽略一些可以作為充分條件但非必要條件的東西,故設計的一些**存在「不完善」的嫌疑。因為為了堵住所有漏洞,往往會讓整個**讓人感覺其重心並非在我想介紹的技術上,而在「苦行僧」式的程式設計原則上。
我們知道c++是乙個型別嚴格的語言,比如下面乙個函式
void test_int_proxy(const int_proxy& v)
呼叫者對其傳參也應該是乙個int_proxy的物件,但是實際情況並非如此。那該如何表述,我個人覺得應該是:編譯器對其傳參應該是乙個int_proxy物件。這兩種表述的區別就是「呼叫者」和「編譯器」的區別。我們來看乙個實際例子,我們先假定int_proxy類這麼定義:
class int_proxy ;
public:
int value() const
private:
int _m;
};該類非常簡單,它有乙個帶引數的建構函式,並使用引數列表形式初始化類的成員變數。
一般情況下我們都會這麼呼叫test_int_proxy方法:
test_int_proxy(int_proxy(100));
這種寫法我想沒人會有異議,但是如果出現下面這種寫法,就可能讓人感覺不可接受了:
test_int_proxy(100);
然而,這種寫法對上述類的定義來說是合法的!其效果和使用int_proxy控制住是一樣的。這是為什麼呢?這便是類建構函式的隱式轉換技術。c++編譯器認為test_int_proxy方法傳入的應該是乙個const型別的int_proxy物件,然而如果它發現引數不是該物件時,就會使用該類中可以使用該引數進行構造物件的方法構造出乙個臨時的物件。我們例子中傳參100是個int型資料,而int_proxy正好有乙個攜帶int引數的建構函式。稍微總結下類建構函式隱式轉換的必要條件:
找不到傳參型別嚴格對應的函式
找到傳參型別嚴格匹配的類的建構函式
因為隱式轉換構造出的是臨時物件,所以不可修改,故觸發隱式轉換的函式的傳參型別必須要使用const修飾
但是個人覺得這種「奇巧淫技」還是不用為好。比如我們**中還有如下函式:
void test_int_proxy(const int& v)
那麼c++編譯器會針對傳100的呼叫上面這個過程。這樣乙個函式呼叫有兩個匹配的呼叫方法就會產生不確定性——這兒指的不確定性並非是指編譯器呼叫哪個方法的不確定性,而是指維護這段**的人對上述**做調整時容易忽略一些問題而導致的「人禍」。
再比如,我們在**中加入下面類和方法
class int_proxy_2 ;
public:
int value() const
private:
int _m;
};void test_int_proxy(const int_proxy_2& v)
那麼編譯器不能確定隱式轉換是要轉換哪個類,更不知道是呼叫哪個test_int_proxy方法了。
限制類建構函式的隱式轉換的方法也很簡單,就是給對應的建構函式加上explict關鍵字
class int_proxy ;
這樣通過隱式轉換而構造臨時物件的圖謀將會被察覺並禁止。
explicit 類建構函式的隱式轉換
限制類編譯器呼叫建構函式的隱式轉換 單一入參建構函式 我們知道c 是乙個型別嚴格的語言,比如下面乙個函式 void test int proxy const int proxy v 呼叫者對其傳參也應該是乙個int proxy的物件,但是實際情況並非如此。那該如何表述,我個人覺得應該是 編譯器對其傳...
建構函式隱式轉換
建構函式會引起乙個不引人注意的問題 用單個實參來呼叫的建構函式定義了從從形參型別到類型別的乙個隱式轉換。舉個例子說 cpp view plain copy class sales item sales item add sales item other sales item const std st...
建構函式 建構函式隱式轉換 拷貝建構函式
建構函式對於我們來說是比較熟悉的,c primer裡提到 類通過乙個或幾個特殊的成員函式來控制其物件的初始化過程,為 建構函式。例1 class fruit 定義乙個類,名字叫fruit 這樣的建構函式是我們比較常見的,但是如果變成 class fruit 定義乙個類,名字叫fruit 即使是乙個類...