您好,登錄后才能下訂單哦!
本篇文章為大家展示了C++中用戶怎么自定義轉換過程,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
傳統轉換方式(Traditional Type-Casting)
C++作為C語言的超集,完全繼承了C語言所具有的類型轉換方法和能力,因此對于這部分在基礎數值類型上的轉換是比較容易理解的。但是因為C++是面向對象的語言,有類的概念,因此讓又多一層需要理解的內容。
隱式轉換 (Implicit Conversion)
隱式轉換不需要任何轉換運算符,編譯器會自動根據類型兼容性進行不同類型之間的轉換。一般情況下,在C/C++中這種轉換多出現在基本數值類型上,其基本原則就是所需內存小的類型可以直接轉換成內存大相同的或者。
內存大小相同的類型之間也可以互相轉換,但是得到的結果可能不是預期的,因而可能會得到編譯器的警告。比如 unsigned int uintVariable = -1; 。
雖說:程序員只在意錯誤(Error),不關心警告(Warning),但是導師也是嚴令禁止在程序中出現的,所以對于這樣的隱式轉換也會盡量避免。
顯示轉換 (Explicit Conversion)
顯示轉換要表明所要轉換的目標對象是什么樣的類型,然后編譯器會做出轉換,它有兩種格式:
C語言格式(C-like Cast) (new_type) expression
函數式(Function-style Cast) new_type (expression)
示例代碼
#include <iostream>using namespace std; int main() {int x0 = 100; float num0 = x0; float num = 98.76; int x1 = (int) num; int x2 = int(num); cout << "num0 = " << num0 << endl; cout << "x1 = " << x1 << endl; cout << "x2 = " << x2 << endl; cout << "x3 = " << x3 << endl;}
對于C++的類而言,也可以在其實例對象上使用傳統的類型轉換
這是利用了C++的一些語言特性。
下邊就以例子來做解釋
代碼
#include<iostream> #include<string> using namespace std; //macro definitions#define IDER_DEBUG 1#define FUNC_TRAC(info) {if(IDER_DEBUG)cout<<"----"<<info<<"----"<<endl;}//class declarationclass Human;class Ape; class Programmer;//class definitionclass Programmer{public: Programmer(string where = "genius") { FUNC_TRAC("Programmer Default Constructor"); from = where; } /*Programmer(Programmer& p) { FUNC_TRAC("Programmer Copy Constructor"); from = p.from; }*/ void Speach(){cout<<"I am a Programmer, I am "<< from <<endl;}private: string from;};class Human {public: Human(string where = "delivered by Parents"):heart("Human with Training") { FUNC_TRAC("Human Default Constructor"); from = where; } Human(Ape& a):heart("Human with Practice") { FUNC_TRAC("Hummer Ape-Promotion Constructor"); from = "Evolution from an Ape"; } operator Programmer() //here is weird, it is really different whether we have "&" or not { FUNC_TRAC("Hummer Programmer-Cast Operator"); return heart; //Programmer("Human with Practice"); // it is not good to return temporary variable } Human& operator =(Human& h) { FUNC_TRAC("Hummer Assignment Operator"); cout<<"Call assignment"<<endl; return *this; } void Speach(){cout<<"I am a Human, I am "<< from <<endl;} private: string from; Programmer heart; //Every one has a heart to be a programmer};class Ape {public: Ape(string where = "from Nature") { FUNC_TRAC("Ape Default Constructor"); from = where; } Ape& operator =(Programmer& p) { FUNC_TRAC("Ape Programmer-Assignment Operator"); from="Degeneration from a Programmer"; return *this; } /*Ape& operator =(Ape& p) { FUNC_TRAC("Ape Assignment Operator"); cout<<"Ape assign"<<endl; return *this; }*/ void Speach(){cout<<"#(*%^, !@#$&)( "<< from <<endl;}private: string from;};//main functionint main(void) { Ape a; //a.Speach(); Human h = a; // using promtion constructor //h.Speach(); Human h3; h3 = a; // Error, no match assignment opeartor Programmer p = h; // using Programmer-cast operaotor //p.Speach(); Programmer p0; p0 = h; // using Programmer-cast operaotor Programmer p1 = h.operator Programmer(); Programmer p2 = Programmer(h); Programmer p3 = (Programmer)h; Ape a2; a2 = p; //using assignment operator //a2.Speach(); Ape a3 = p; // Error, no match constructor return 0;}
在這個例子中,我定義了三個類,這三個類之間沒有繼承和被繼承的關系,也沒有friend關系,其基本聯系就是:Ape可以進化成為Human,Human經過不斷的訓練就可以成為Programmer,不過Programmer也有可能退化變成Ape。
分析
從main函數中他們進行的轉換操作,可以看出這是一種隱式的轉換。不過三個類的對象之間能夠實現轉換的真正原因卻并不相同。
首先,從Ape到Human的轉換方式
Human h = a;
其實是調用了Human的promotion構造函數
Human(Ape& a);
這個函數接受了Ape作為構造參數,實例化了一個Human對象。
從Human到 Programmer,則是因為我在Human中定義了一個到Programmer的轉換運算符:
operator Programmer()
因此,在main函數中的兩個賦值語句:
Programmer p = h; p0 = h;
都是調用了這個轉換函數。
從Programmer退化到Ape是一件很不情愿的事情(就因為在中文里,我們是程序猿),在代碼中的實現方式,則是在Ape類中定義了一個接受Programmer引用作為參數的Assignment運算符的重載形式。
Ape& operator =(Programmer& p)
于是下邊的語句
a2 = p;
就得以在程序中運行了
進一步分析
已經看到了Ape, Human,Programmer的之間的轉換都是使用了不同的C++特性,調用的是不同的方法。但是深究起來,這些方法還是各有個的不同。
以Human到Programmer為基準,這個轉換用的是用戶自定義轉換(user-defined cast),因此可以說這種方式才是真正的類型之間的轉換。
也因此我們在main中看到了兩種語法格式的轉換都是有效的:
定義并初始化
Programmer p = h;
賦值
p0 = h;
但是Ape到Human的轉換調用的構造函數,因此它只有在類實例化對象并初始化的時候才有效,也因此下邊的語句會得到編譯器的錯誤:
Human h3; h3 = a; // Error, no match assignment opeartor
因為Human從出生就知道自己是怎么來的,不應該后來才發現自己不是媽生的(當然,這些情況不是不可能的,比如“人猿泰山”)。
而Programmer到Ape是后天形成的,不然一出生就變成猴子了,那就只能用腳趾了Coding了。所以以下代碼也是編譯不過的:
Ape a3 = p; // Error, no match constructor
在回過來講講Human到Programmer,我們還可以用更多不同的形式來寫,比如兩種形式的顯示轉換:
Programmer p1 = Programmer(h); Programmer p2 = (Programmer)h;
(是初始化還是賦值都無所謂)
但是真正編譯之后,其格式應該是:
Programmer p3 = h.operator Programmer();
對于Assignment運算符其實也是如此,真正調用的還是:
a2.operator =(p);
后記
其實在實際編程中,可能受到了C#的影響(因為C#的初始化并不是得到一個全新的對象,而只是獲得引用),并不會經常使用到用戶自定義轉換,也很少重載一個接受非自身對象引用的Assignment運算符。
真正要轉換的時候,多數還是通過構造函數進行。或者是,實例化好對象,通過兩者的接口對數據進行賦值。畢竟以上講到各種方式中,也只能調用到接收到對象的外部接口,不能進行私有數據的操作。
關于數值類型的轉換和類對象的轉換,前面都已經提到了,但似乎還遺漏了什么?
是的,C++還有引用類型(reference)和指針類型(pointer)。這兩者在不同類型之間的轉換還沒有說。
在C++中,指針類型似乎是被視為是沒有差異的,想想的確如此,因為它只是存放所指對象的地址,因此所需內存空間、格式都是一致的,也因此在C++不同類型 之間的指針是可以隨意轉換的,一般需要用顯示轉換。但是這種轉換是沒有意義,因為地址所指的類型并非所需要的類型,通過該地址進行偏移找到的數據或方法就不會是我們所需要的了,在運行的時候,程序就會發生異常。
對于引用類型,在沒有繼承關系的類型之間進行轉換似乎也并不合理,引用其實在實現上可以被視為指針,只是在語法格式上,它被當做對象使用。如果進行引用類型的轉換,我們到底是想要一個新的對象呢,還是只要地址?讓人迷糊。
另外,指針和引用的應該是用在已經存在的對象或對象變量上。因此如果是轉換中返回轉換運算符的方法之內的一個局部變量(像Human類的operator Programmer()方法中我注釋掉的那行代碼),那么在離開轉換運算符的方法之后,那些變量就會被回收,在指向那些地址也是沒有意義了;如果是在內部new一個心的對象,這個對象的管理就變得沒有約束性,我們不知道該在何時會去delete它;即使像我實現的那樣,在Human類中帶著一個Programmer的對象Heart,但是這個設計似乎也并不是非常好的,因為不能保證每個人都有一顆作為程序員的心。
遺留問題
前面也提到了指針和引用在類型轉換上的問題,因此對于用戶自定義轉換符,在我的代碼中,我所使用的是基于對象的轉換:
operator Programmer();
不是基于指針:
operator Programmer*();
也不是基于引用
operator Programmer&()
在我看來,這是合理的,也是合適的。
但是如果我在Programmer類中定義了一個copy構造函數,那么無論以上提到4種的從Human到Programmer的代碼格式都得到編譯錯誤。
這似乎可以理解:編譯器會從構造函數中發現合適的入口,但是它失敗了,所以就錯誤了。
但是為何
h.operator Programmer();
上述內容就是C++中用戶怎么自定義轉換過程,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。