(原创)c++中的类型擦除
c++11 boost技術(shù)交流群:296561497,歡迎大家來交流技術(shù)。
關(guān)于類型擦除,可能很多人都不清楚,不知道類型擦除是干啥的,為什么需要類型擦除。有必要做個說明,類型擦除就是將原有類型消除或者隱藏。為什么要擦除類型?因?yàn)楹芏鄷r候我不關(guān)心具體類型是什么或者根本就不需要這個類型,通過類型擦除我們可以獲取很多好處,比如使得我們的程序有更好的擴(kuò)展性、還能消除耦合以及消除一些重復(fù)行為,使程序更加簡潔高效。歸納一下c++中類型擦除方式主要有如下五種:
第一種:通過多態(tài)來擦除類型
第二種:通過模板來擦除類型
第三種:通過某種容器來擦除類型
第四種:通過某種通用類型來擦除類型
第五種:通過閉包來擦除類型
第一種類型隱藏的方式最簡單也是我們經(jīng)常用的,通過將派生類型隱式轉(zhuǎn)換成基類型,再通過基類去多態(tài)的調(diào)用行為,在這種情況下,我不用關(guān)心派生類的具體類型,我只需要以一種統(tǒng)一的方式去做不同的事情,所以就把派生類型轉(zhuǎn)成基類型隱藏起來,這樣不僅僅可以多態(tài)調(diào)用還使我們的程序具有良好的可擴(kuò)展性。然而這種方式的類型擦除僅僅是部分的類型擦除,因?yàn)榛愋腿匀淮嬖?#xff0c;而且這種類型擦除的方式還必須是繼承方式的才可以,而且繼承使得兩個對象強(qiáng)烈的耦合在一起了,正是因?yàn)檫@些缺點(diǎn),通過多態(tài)來擦除類型的方式有較多局限性效果也不好。這時我們通過第二種方式擦除類型,以解決第一種方式的一些缺點(diǎn)。通過模板來擦除類型,本質(zhì)上是把不同類型的共同行為進(jìn)行了抽象,這時不同類型彼此之間不需要通過繼承這種強(qiáng)耦合的方式去獲得共同的行為了,僅僅是通過模板就能獲取共同行為,降低了不同類型之間的耦合,是一種很好的類型擦除方式。然而,第二種方式雖然降低了對象間的耦合,但是還有一個問題沒解決,就是基本類型始終需要指定,并沒有消除基本類型,例如,我不可能把一個T本身作為容器元素,必須在容器初始化時就要知名這個T是具體某個類型。這時多么希望有一種通用的類型啊,可以讓我的容器容納所有的類型,就像c#和java中的object類型一樣,是所有類型的基類。c++中沒有這種object類型怎么辦?也許有人想到了,可以用boost.variant類型,是的,boost.variant可以把各種不同的類型包起來,從而讓我們獲得了一種統(tǒng)一的類型,而且不同類型的對象間沒有耦合關(guān)系,它僅僅是一個類型的容器。讓我們看看怎么用boost.variant來擦除類型。
struct blob {const char *pBuf;int size; }; //定義通用的類型,這個類型可能容納多種類型 typedef boost::variant<double, int, uint32_t, sqlite3_int64, char*, blob, NullType>Value;vector<Value> vt; //通用類型的容器,這個容器現(xiàn)在就可以容納上面的那些類型的對象了 vt.push_back(1); vt.push_back("test"); vt.push_back(1.22); vt.push_back({"test", 4}); View Code上面的代碼就擦除了不同類型,使得不同的類型都可以放到一個容器中了,如果要取出來就很簡單,通過get<T>(Value)就可以獲取對應(yīng)類型的值了。這種方式是通過某種容器把類型包起來了,從而達(dá)到類型擦除的目的。它的缺點(diǎn)是這個通用的類型必須事先定義好,它只能容納聲明的那些類型,增加一種新類型就不行了。通過第四種方式可以消除這個缺點(diǎn),通過某種通用類型來擦除類型。類似于c#和java中的object類型。這種通用類型是通過boost.any實(shí)現(xiàn)的,它不需要預(yù)先定義類型,不同類型都可以轉(zhuǎn)成any。讓我們看看怎么用any來擦除類型的。
unordered_map<string, boost::any> m_creatorMap; m_creatorMap.insert(make_pair(strKey, new T)); //T may be any type boost::any obj = m_creatorMap[strKey]; T t = boost::any_cast<T>(obj);需要注意的是,第四和第五種方式雖然解決了第三種方式不能徹底消除基本類型的缺點(diǎn),但是還存一個缺點(diǎn),就是取值的時候仍然依賴于具體類型,無論我是通過get<T>還是any_case<T>,我都要T的具體類型,這在某種情況下仍然有局限性。例如,有這樣一種場景:
我有A、B、C、D四種結(jié)構(gòu)體,每個結(jié)構(gòu)體中有某種類型的指針,名稱且稱為info,我現(xiàn)在提供了返回這些結(jié)構(gòu)體的四個接口供外接使用,有可能是c#或者dephi調(diào)用這些接口,由于結(jié)構(gòu)體中的info指針是我分配的內(nèi)存,所以我必須提供釋放這些指針的接口。代碼如下:
大家可以看到,增加的四個刪除函數(shù)內(nèi)部都是重復(fù)代碼,本來通過模板函數(shù)一行搞定,但是沒辦法,c#可沒有c++的模板,還得老老實(shí)實(shí)的提供這些重復(fù)行為的接口,而且這種方式還有個壞處就是每增加一種類型就得增加一個重復(fù)的刪除接口,怎么辦?能統(tǒng)一成一個刪除接口嗎?可以,一個可行的辦法就是將分配的內(nèi)存通過一個ID關(guān)聯(lián)并保存起來,讓外接傳一個ID,告訴我要刪那塊內(nèi)存,新的統(tǒng)一刪除函數(shù)可能是這樣:
//內(nèi)部將分配的內(nèi)存存到map中,讓外面?zhèn)鱅D,內(nèi)部通過ID去刪除對應(yīng)的內(nèi)存塊 map<int, T> mapT;template<typename R, typename T> R GetT() {R result{1,new T()};mapT.insert(std::pair<int, T>(1, R)); return result; }//通過ID去關(guān)聯(lián)我分配的內(nèi)存塊,外面?zhèn)鱅D,內(nèi)部通過ID去刪除關(guān)聯(lián)的內(nèi)存塊 void DeleteT(const int& id) {R t = mapT[id]->second();delete t.info; }很遺憾,上面的代碼編譯不過,因?yàn)?#xff0c;map<int, T> mapT只能保存一種類型的對象,無法把分配的不同類型的對象保存起來,我們可以通過方式三和方式四,用variant或者any去擦除類型,解決T不能代表多種類型的問題,第一個問題解決。但是還有第二個問題,DeleteT時,從map中返回的variant或者any,無法取出來,因?yàn)榻涌诤瘮?shù)中沒有類型信息,而取值方法get<T>和any_cast<T>都需要一個具體類型。似乎進(jìn)入了死胡同,無法只提供一個刪除接口了。但是辦法總還是有的。
方式五隆重登場了,看似無解的問題,通過方式五就能解決了。通過閉包來擦除類型很好很強(qiáng)大。在介紹方式五之前,我要先介紹一下閉包,閉包也可以稱為匿名函數(shù)或者lamda表達(dá)式,c++11中的lamda表達(dá)式就是c++中的閉包,c++11引入lamda,實(shí)際上引入了函數(shù)式編程的概念,函數(shù)式編程有很多優(yōu)點(diǎn),使代碼更簡潔,而且聲明式的編碼方式更貼近人的思維方式。函數(shù)式編程在更高的層次上對不同類型的公共行為進(jìn)行了抽象,從而使我們不必去關(guān)心具體類型。關(guān)于函數(shù)式編程的優(yōu)點(diǎn)就不多說了。下面看看如何使用方式五去解決上面的問題。
總結(jié):通過閉包去擦除類型,可以解決前面四種擦除方式遇到的問題,優(yōu)雅而簡單!
轉(zhuǎn)載于:https://www.cnblogs.com/qicosmos/p/3256022.html
總結(jié)
以上是生活随笔為你收集整理的(原创)c++中的类型擦除的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Cocoa touch(六):UIVie
- 下一篇: 嵌入式培训学习历程第二十二天