溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

C++中用戶(hù)怎么自定義轉(zhuǎn)換過(guò)程

發(fā)布時(shí)間:2021-07-20 14:18:10 來(lái)源:億速云 閱讀:178 作者:Leah 欄目:編程語(yǔ)言

本篇文章為大家展示了C++中用戶(hù)怎么自定義轉(zhuǎn)換過(guò)程,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。

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

C++作為C語(yǔ)言的超集,完全繼承了C語(yǔ)言所具有的類(lèi)型轉(zhuǎn)換方法和能力,因此對(duì)于這部分在基礎(chǔ)數(shù)值類(lèi)型上的轉(zhuǎn)換是比較容易理解的。但是因?yàn)镃++是面向?qū)ο蟮恼Z(yǔ)言,有類(lèi)的概念,因此讓又多一層需要理解的內(nèi)容。

隱式轉(zhuǎn)換 (Implicit Conversion)

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

內(nèi)存大小相同的類(lèi)型之間也可以互相轉(zhuǎn)換,但是得到的結(jié)果可能不是預(yù)期的,因而可能會(huì)得到編譯器的警告。比如 unsigned int uintVariable = -1; 。

雖說(shuō):程序員只在意錯(cuò)誤(Error),不關(guān)心警告(Warning),但是導(dǎo)師也是嚴(yán)令禁止在程序中出現(xiàn)的,所以對(duì)于這樣的隱式轉(zhuǎn)換也會(huì)盡量避免。

顯示轉(zhuǎn)換 (Explicit Conversion)

顯示轉(zhuǎn)換要表明所要轉(zhuǎn)換的目標(biāo)對(duì)象是什么樣的類(lèi)型,然后編譯器會(huì)做出轉(zhuǎn)換,它有兩種格式:

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

函數(shù)式(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;}

對(duì)于C++的類(lèi)而言,也可以在其實(shí)例對(duì)象上使用傳統(tǒng)的類(lèi)型轉(zhuǎn)換

這是利用了C++的一些語(yǔ)言特性。

下邊就以例子來(lái)做解釋

代碼

#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;}

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

分析

從main函數(shù)中他們進(jìn)行的轉(zhuǎn)換操作,可以看出這是一種隱式的轉(zhuǎn)換。不過(guò)三個(gè)類(lèi)的對(duì)象之間能夠?qū)崿F(xiàn)轉(zhuǎn)換的真正原因卻并不相同。

首先,從Ape到Human的轉(zhuǎn)換方式

Human h = a;

其實(shí)是調(diào)用了Human的promotion構(gòu)造函數(shù)

Human(Ape& a);

這個(gè)函數(shù)接受了Ape作為構(gòu)造參數(shù),實(shí)例化了一個(gè)Human對(duì)象。

從Human到 Programmer,則是因?yàn)槲以贖uman中定義了一個(gè)到Programmer的轉(zhuǎn)換運(yùn)算符:

operator Programmer()

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

Programmer p = h;  p0 = h;

都是調(diào)用了這個(gè)轉(zhuǎn)換函數(shù)。

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

Ape& operator =(Programmer& p)

于是下邊的語(yǔ)句

a2 = p;

就得以在程序中運(yùn)行了

進(jìn)一步分析

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

以Human到Programmer為基準(zhǔn),這個(gè)轉(zhuǎn)換用的是用戶(hù)自定義轉(zhuǎn)換(user-defined cast),因此可以說(shuō)這種方式才是真正的類(lèi)型之間的轉(zhuǎn)換。

也因此我們?cè)趍ain中看到了兩種語(yǔ)法格式的轉(zhuǎn)換都是有效的:

定義并初始化

Programmer p = h;

賦值

p0 = h;

但是Ape到Human的轉(zhuǎn)換調(diào)用的構(gòu)造函數(shù),因此它只有在類(lèi)實(shí)例化對(duì)象并初始化的時(shí)候才有效,也因此下邊的語(yǔ)句會(huì)得到編譯器的錯(cuò)誤:

Human h3;  h3 = a; // Error, no match assignment opeartor

因?yàn)镠uman從出生就知道自己是怎么來(lái)的,不應(yīng)該后來(lái)才發(fā)現(xiàn)自己不是媽生的(當(dāng)然,這些情況不是不可能的,比如“人猿泰山”)。

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

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

在回過(guò)來(lái)講講Human到Programmer,我們還可以用更多不同的形式來(lái)寫(xiě),比如兩種形式的顯示轉(zhuǎn)換:

Programmer p1 = Programmer(h);  Programmer p2 = (Programmer)h;

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

但是真正編譯之后,其格式應(yīng)該是:

Programmer p3 = h.operator Programmer();

對(duì)于A(yíng)ssignment運(yùn)算符其實(shí)也是如此,真正調(diào)用的還是:

a2.operator =(p);

后記

其實(shí)在實(shí)際編程中,可能受到了C#的影響(因?yàn)镃#的初始化并不是得到一個(gè)全新的對(duì)象,而只是獲得引用),并不會(huì)經(jīng)常使用到用戶(hù)自定義轉(zhuǎn)換,也很少重載一個(gè)接受非自身對(duì)象引用的Assignment運(yùn)算符。

真正要轉(zhuǎn)換的時(shí)候,多數(shù)還是通過(guò)構(gòu)造函數(shù)進(jìn)行?;蛘呤牵瑢?shí)例化好對(duì)象,通過(guò)兩者的接口對(duì)數(shù)據(jù)進(jìn)行賦值。畢竟以上講到各種方式中,也只能調(diào)用到接收到對(duì)象的外部接口,不能進(jìn)行私有數(shù)據(jù)的操作。

關(guān)于數(shù)值類(lèi)型的轉(zhuǎn)換和類(lèi)對(duì)象的轉(zhuǎn)換,前面都已經(jīng)提到了,但似乎還遺漏了什么?

是的,C++還有引用類(lèi)型(reference)和指針類(lèi)型(pointer)。這兩者在不同類(lèi)型之間的轉(zhuǎn)換還沒(méi)有說(shuō)。

在C++中,指針類(lèi)型似乎是被視為是沒(méi)有差異的,想想的確如此,因?yàn)樗皇谴娣潘笇?duì)象的地址,因此所需內(nèi)存空間、格式都是一致的,也因此在C++不同類(lèi)型 之間的指針是可以隨意轉(zhuǎn)換的,一般需要用顯示轉(zhuǎn)換。但是這種轉(zhuǎn)換是沒(méi)有意義,因?yàn)榈刂匪傅念?lèi)型并非所需要的類(lèi)型,通過(guò)該地址進(jìn)行偏移找到的數(shù)據(jù)或方法就不會(huì)是我們所需要的了,在運(yùn)行的時(shí)候,程序就會(huì)發(fā)生異常。

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

另外,指針和引用的應(yīng)該是用在已經(jīng)存在的對(duì)象或?qū)ο笞兞可稀R虼巳绻寝D(zhuǎn)換中返回轉(zhuǎn)換運(yùn)算符的方法之內(nèi)的一個(gè)局部變量(像Human類(lèi)的operator Programmer()方法中我注釋掉的那行代碼),那么在離開(kāi)轉(zhuǎn)換運(yùn)算符的方法之后,那些變量就會(huì)被回收,在指向那些地址也是沒(méi)有意義了;如果是在內(nèi)部new一個(gè)心的對(duì)象,這個(gè)對(duì)象的管理就變得沒(méi)有約束性,我們不知道該在何時(shí)會(huì)去delete它;即使像我實(shí)現(xiàn)的那樣,在Human類(lèi)中帶著一個(gè)Programmer的對(duì)象Heart,但是這個(gè)設(shè)計(jì)似乎也并不是非常好的,因?yàn)椴荒鼙WC每個(gè)人都有一顆作為程序員的心。

遺留問(wèn)題

前面也提到了指針和引用在類(lèi)型轉(zhuǎn)換上的問(wèn)題,因此對(duì)于用戶(hù)自定義轉(zhuǎn)換符,在我的代碼中,我所使用的是基于對(duì)象的轉(zhuǎn)換:

operator Programmer();

不是基于指針:

operator Programmer*();

也不是基于引用

operator Programmer&()

在我看來(lái),這是合理的,也是合適的。

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

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

但是為何

h.operator Programmer();

上述內(nèi)容就是C++中用戶(hù)怎么自定義轉(zhuǎn)換過(guò)程,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

c++
AI