自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

詳解C++用戶自定義轉換過程

開發(fā) 后端
今天我們要討論的是C++的轉換方式,這樣的轉換方式跟C語言有所不同,C++是面向對象的語言,有類的概念,因此讓又多一層需要理解的內容。

在計算機語言中,類型的存在讓我們可以更有針對性的進行數(shù)據(jù)和功能的處理,但是卻又存在了類型轉化的問題。C++如同其他計算機語言一樣,也同樣都這些問題。不過它相對于C而言多了引用類型(Reference);相對與C#來講,又多了指針類型(Point)。這似乎讓它的類型轉化變得更加撲朔迷離。

也許是C方面知識的延續(xù),我對C++的基礎類型之間的轉換還算比較清楚的,但是由于C#的Convert這個類庫是那么的好用,以至于我一直不太適應C++的轉換方式。不過經(jīng)過導師的教授,發(fā)現(xiàn)C++的也可以構建禇類似Convert的轉換方式。

在導師經(jīng)過一堂課關于C++類型轉換的課后,我又在網(wǎng)上查閱相關資料,以便更清晰詳細的總結C++在類型轉換的方式。

傳統(tǒng)轉換方式(Traditional Type-Casting)

C++作為C語言的超集,完全繼承了C語言所具有的類型轉換方法和能力,因此對于這部分在基礎數(shù)值類型上的轉換是比較容易理解的。但是因為C++是面向對象的語言,有類的概念,因此讓又多一層需要理解的內容。

隱式轉換 (Implicit Conversion)

隱式轉換不需要任何轉換運算符,編譯器會自動根據(jù)類型兼容性進行不同類型之間的轉換。一般情況下,在C/C++中這種轉換多出現(xiàn)在基本數(shù)值類型上,其基本原則就是所需內存小的類型可以直接轉換成內存大相同的或者。

內存大小相同的類型之間也可以互相轉換,但是得到的結果可能不是預期的,因而可能會得到編譯器的警告。比如 unsigned int uintVariable = -1; 。

雖說:程序員只在意錯誤(Error),不關心警告(Warning),但是導師也是嚴令禁止在程序中出現(xiàn)的,所以對于這樣的隱式轉換也會盡量避免。

顯示轉換 (Explicit Conversion)

顯示轉換要表明所要轉換的目標對象是什么樣的類型,然后編譯器會做出轉換,它有兩種格式:

C語言格式(C-like Cast) (new_type) expression

函數(shù)式(Function-style Cast) new_type (expression)

示例代碼

  1. #include <iostream>using namespace std;      
  2. int main() {int x0 = 100;      
  3. float num0 = x0;      
  4. float num = 98.76;      
  5. int x1 = (int) num;      
  6. int x2 = int(num);          
  7. cout << "num0 = " << num0 << endl;      
  8. cout << "x1 = " << x1 << endl;      
  9. cout << "x2 = " << x2 << endl;      
  10. cout << "x3 = " << x3 << endl;} 

對于C++的類而言,也可以在其實例對象上使用傳統(tǒng)的類型轉換

這是利用了C++的一些語言特性。

下邊就以例子來做解釋

代碼

  1. #include<iostream>  
  2. #include<string>  
  3. using namespace std;  
  4. //macro definitions#define    
  5. IDER_DEBUG 1#define FUNC_TRAC(info)   
  6. {if(IDER_DEBUG)cout<<"----"<<info<<"----"<<endl;}
  7. //class declarationclass Human;class Ape;  
  8. class Programmer;//class definitionclass Programmer{public:      
  9. Programmer(string where = "genius")     
  10.  {          
  11. FUNC_TRAC("Programmer Default Constructor");         from = where;      
  12. }     
  13.  /*Programmer(Programmer& p)     
  14.  {        
  15.   FUNC_TRAC("Programmer Copy Constructor");          
  16.  from = p.from;     
  17.  }*/     
  18.  void Speach(){cout<<"I am a Programmer, I am "<< from <<endl;}private:     
  19.  string from;};class Human {public:     
  20.  Human(string where = "delivered by Parents"):heart("Human with Training")     
  21.  {         
  22.  FUNC_TRAC("Human Default Constructor");         
  23.   from = where;    }      
  24. Human(Ape& a):heart("Human with Practice")     
  25.  {        FUNC_TRAC("Hummer Ape-Promotion Constructor");       
  26.    from = "Evolution from an Ape";    
  27.   }       
  28. operator Programmer() 
  29. //here is weird, it is really different whether we have "&" or not    
  30.   {         
  31.  FUNC_TRAC("Hummer Programmer-Cast Operator");     
  32.     return heart;        
  33.    //Programmer("Human with Practice");  
  34.  // it is not good to return temporary variable    
  35.   }     
  36.  Human& operator =(Human& h)    {      
  37.     FUNC_TRAC("Hummer Assignment Operator");       
  38.   cout<<"Call assignment"<<endl;       
  39.     return *this;    
  40.   }     
  41.  void Speach(){cout<<"I am a Human, I am "<< from <<endl;}    
  42.   private:     
  43.  string from;     
  44.  Programmer heart; //Every one has a heart to be a programmer};class Ape {public:     
  45.  Ape(string where = "from Nature")    
  46.   {       
  47.    FUNC_TRAC("Ape Default Constructor");      
  48.      from = where;    
  49.   }      
  50. Ape& operator =(Programmer& p)     
  51.  {         
  52.  FUNC_TRAC("Ape Programmer-Assignment Operator");          
  53.  from="Degeneration from a Programmer";        
  54.    return *this;    
  55.   }   
  56.    /*Ape& operator =(Ape& p)     
  57.   {        
  58.   FUNC_TRAC("Ape Assignment Operator");      
  59.    cout<<"Ape assign"<<endl;       
  60.     return *this;     
  61.   }*/     
  62.  void Speach(){cout<<"#(*%^, !@#$&)( "<< from <<endl;}private:     
  63.  string from;};//main functionint main(void) {   
  64.    Ape a;     
  65.  //a.Speach();    
  66.    Human h = a; // using promtion constructor     
  67.  //h.Speach();       
  68.    Human h2;     
  69.  h2 = a; // Error, no match assignment opeartor    
  70.     Programmer p = h; // using Programmer-cast operaotor    
  71.   //p.Speach();     
  72.  Programmer p0;    
  73.   p0 = h;     
  74.  // using  Programmer-cast operaotor       
  75.    Programmer p1 = h.operator Programmer();   
  76.  Programmer p2 = Programmer(h);   
  77.    Programmer p3 = (Programmer)h;    
  78.   Ape a2;     
  79.  a2 = p; //using assignment operator    
  80.   //a2.Speach();      
  81.     Ape a3 = p; // Error, no match constructor   
  82.    return 0;} 

在這個例子中,我定義了三個類,這三個類之間沒有繼承和被繼承的關系,也沒有friend關系,其基本聯(lián)系就是:Ape可以進化成為Human,Human經(jīng)過不斷的訓練就可以成為Programmer,不過Programmer也有可能退化變成Ape。

分析

從main函數(shù)中他們進行的轉換操作,可以看出這是一種隱式的轉換。不過三個類的對象之間能夠實現(xiàn)轉換的真正原因卻并不相同。

首先,從Ape到Human的轉換方式

  1. Human h = a; 

其實是調用了Human的promotion構造函數(shù)

  1. Human(Ape& a); 

這個函數(shù)接受了Ape作為構造參數(shù),實例化了一個Human對象。

從Human到 Programmer,則是因為我在Human中定義了一個到Programmer的轉換運算符:

operator Programmer()

因此,在main函數(shù)中的兩個賦值語句:

  1. Programmer p = h;  
  2. p0 = h; 

都是調用了這個轉換函數(shù)。

從Programmer退化到Ape是一件很不情愿的事情(就因為在中文里,我們是程序猿),在代碼中的實現(xiàn)方式,則是在Ape類中定義了一個接受Programmer引用作為參數(shù)的Assignment運算符的重載形式。

Ape& operator =(Programmer& p)

于是下邊的語句

a2 = p;

就得以在程序中運行了

進一步分析

已經(jīng)看到了Ape, Human,Programmer的之間的轉換都是使用了不同的C++特性,調用的是不同的方法。但是深究起來,這些方法還是各有個的不同。

以Human到Programmer為基準,這個轉換用的是用戶自定義轉換(user-defined cast),因此可以說這種方式才是真正的類型之間的轉換。

也因此我們在main中看到了兩種語法格式的轉換都是有效的:

定義并初始化

Programmer p = h;

賦值

p0 = h;

但是Ape到Human的轉換調用的構造函數(shù),因此它只有在類實例化對象并初始化的時候才有效,也因此下邊的語句會得到編譯器的錯誤:

  1. Human h2;  
  2. h2 = a; // Error, no match assignment opeartor 

因為Human從出生就知道自己是怎么來的,不應該后來才發(fā)現(xiàn)自己不是媽生的(當然,這些情況不是不可能的,比如“人猿泰山”)。

而Programmer到Ape是后天形成的,不然一出生就變成猴子了,那就只能用腳趾了Coding了。所以以下代碼也是編譯不過的:

  1. Ape a3 = p; // Error, no match constructor 

在回過來講講Human到Programmer,我們還可以用更多不同的形式來寫,比如兩種形式的顯示轉換:

  1. Programmer p1 = Programmer(h);  
  2. Programmer p2 = (Programmer)h; 

(是初始化還是賦值都無所謂)

但是真正編譯之后,其格式應該是:

Programmer p3 = h.operator Programmer();

對于Assignment運算符其實也是如此,真正調用的還是:

a2.operator =(p);

后記

其實在實際編程中,可能受到了C#的影響(因為C#的初始化并不是得到一個全新的對象,而只是獲得引用),并不會經(jīng)常使用到用戶自定義轉換,也很少重載一個接受非自身對象引用的Assignment運算符。

真正要轉換的時候,多數(shù)還是通過構造函數(shù)進行?;蛘呤牵瑢嵗脤ο?,通過兩者的接口對數(shù)據(jù)進行賦值。畢竟以上講到各種方式中,也只能調用到接收到對象的外部接口,不能進行私有數(shù)據(jù)的操作。

關于數(shù)值類型的轉換和類對象的轉換,前面都已經(jīng)提到了,但似乎還遺漏了什么?

是的,C++還有引用類型(reference)和指針類型(pointer)。這兩者在不同類型之間的轉換還沒有說。

在C++中,指針類型似乎是被視為是沒有差異的,想想的確如此,因為它只是存放所指對象的地址,因此所需內存空間、格式都是一致的,也因此在C++不同類型 之間的指針是可以隨意轉換的,一般需要用顯示轉換。但是這種轉換是沒有意義,因為地址所指的類型并非所需要的類型,通過該地址進行偏移找到的數(shù)據(jù)或方法就不會是我們所需要的了,在運行的時候,程序就會發(fā)生異常。

對于引用類型,在沒有繼承關系的類型之間進行轉換似乎也并不合理,引用其實在實現(xiàn)上可以被視為指針,只是在語法格式上,它被當做對象使用。如果進行引用類型的轉換,我們到底是想要一個新的對象呢,還是只要地址?讓人迷糊。

另外,指針和引用的應該是用在已經(jīng)存在的對象或對象變量上。因此如果是轉換中返回轉換運算符的方法之內的一個局部變量(像Human類的operator Programmer()方法中我注釋掉的那行代碼),那么在離開轉換運算符的方法之后,那些變量就會被回收,在指向那些地址也是沒有意義了;如果是在內部new一個心的對象,這個對象的管理就變得沒有約束性,我們不知道該在何時會去delete它;即使像我實現(xiàn)的那樣,在Human類中帶著一個Programmer的對象Heart,但是這個設計似乎也并不是非常好的,因為不能保證每個人都有一顆作為程序員的心。

遺留問題

前面也提到了指針和引用在類型轉換上的問題,因此對于用戶自定義轉換符,在我的代碼中,我所使用的是基于對象的轉換:

operator Programmer();

不是基于指針:

operator Programmer*();

也不是基于引用

operator Programmer&()

在我看來,這是合理的,也是合適的。

但是如果我在Programmer類中定義了一個copy構造函數(shù),那么無論以上提到4種的從Human到Programmer的代碼格式都得到編譯錯誤。

這似乎可以理解:編譯器會從構造函數(shù)中發(fā)現(xiàn)合適的入口,但是它失敗了,所以就錯誤了。

但是為何

h.operator Programmer();

的格式也是錯誤就讓我十分的不解了。

再進一步,如果我把基于對象的轉換改成基于引用的轉換,編譯器就沒有報出錯誤了。但是我認為這個在邏輯上應該是不對的。

C++真的一門復雜的語言(當然跟JavaScript還是沒得比的),對于這個問題,如果讀者你知道這方面的原因,還請你能跟我分享一下你的理解和看法。

原文鏈接:http://www.cnblogs.com/ider/archive/2011/07/03/cpp_cast_operator_part1.html

【編輯推薦】

  1. C++中static的用法總結
  2. C++內存管理的探討
  3. C++的輸出格式控制技巧分析
  4. C++多態(tài)技術的實現(xiàn)和反思
  5. C++中的指針用法匯集
責任編輯:彭凡 來源: 博客園
相關推薦

2009-08-03 13:39:46

C#自定義用戶控件

2022-06-20 08:26:39

Spring容器類型轉換

2021-06-17 06:52:37

C#自定義異常

2009-08-12 14:53:50

C#類型轉換函數(shù)

2010-02-03 16:56:20

C++自定義類

2009-10-09 14:24:27

2009-08-04 08:58:01

C#自定義特性

2009-09-11 11:04:23

C# WinForm自

2024-10-14 17:18:27

2011-08-02 11:17:13

iOS開發(fā) View

2009-07-06 16:20:50

JSP自定義標簽

2018-03-16 08:41:16

Oracle存儲函數(shù)

2009-09-03 15:46:57

C#自定義事件

2010-10-20 16:07:48

SQL用戶自定義角色

2009-03-25 09:43:51

開發(fā)自定義托管WCF

2011-03-02 10:24:23

DashboardAndroid用戶界面設計模板

2023-08-26 19:04:40

配置write轉換器

2011-07-05 18:51:51

QT 控件 鼠標

2009-08-04 13:35:16

ASP.NET自定義樣

2011-08-09 17:16:56

CoreAnimati動畫
點贊
收藏

51CTO技術棧公眾號