Expression Template(表达式模板,ET)
1.前言
在前一篇文章自己實現(xiàn)簡單的string類中提到在實現(xiàn)+操作符重載函數(shù)時,為了防止返回時生成的臨時對象調(diào)用拷貝構(gòu)造函數(shù)動態(tài)申請內(nèi)存空間,使用了一個叫move的函數(shù),它是C++0x新增的特性。既然是C++0x新增的特性,那么在以前沒有這個特性的情況下,對于臨時對象動態(tài)申請內(nèi)存空間的問題是不是可以有其它的方法解決或避免呢?答案是肯定的,可以用Expression Template(表達(dá)式模板,ET)來解決。
2.表達(dá)式模板
對于前面的String類,我們可能經(jīng)常會使用下面的表達(dá)式:
String str4 = str1 + str2 + str3;
這個表達(dá)式有一個問題,就是產(chǎn)生了“不必要”的臨時對象。因為 str1 + str2 的結(jié)果會存放在一個臨時對象 temp1上,然后temp1 + str3的結(jié)果會存放在令一個臨時對象temp2上,temp2最后把結(jié)果傳給str4 進(jìn)行初始化。如果這些向量很長,或者表達(dá)式再加幾節(jié),不僅會產(chǎn)生很多的臨時對象,而且要動態(tài)申請內(nèi)存空間來存放String內(nèi)部的字符串,這很明顯是低效的。move函數(shù)解決了當(dāng)+操作符重載函數(shù)返回臨時對象時,編譯器在棧中為其拷貝另一個對象的開銷(由于+操作符重載函數(shù)內(nèi)聲明的臨時對象temp1在函數(shù)的作用域外事無法使用的,所以編譯器自動的在調(diào)用+操作符重載函數(shù)的域內(nèi)聲明了一個臨時對象temp11,用以存放拷貝的temp1),但并沒有解決產(chǎn)生臨時對象的問題。表達(dá)式模板的思路很簡單,就是對+操作進(jìn)行推遲計算,即一次性計算所有的+操作,這樣就不需要產(chǎn)生臨時對象來保存+操作的臨時結(jié)果。由于要推遲計算,所以必須保存str1,str2,str3操作數(shù)用于后面真正計算推遲的+操作。
3.基于String的表達(dá)式模板實現(xiàn)
雖然表達(dá)式模板的思路很簡單,但其實現(xiàn)確不是想象的那么簡單。原來的做法中,operator + 直接進(jìn)行了計算,既然我們不想它“過早”的計算,那么我們就必須重新重載一個operator + 運算符,在這個運算中不進(jìn)行真正的運算,只是生成一個對象,在這個對象中把加法運算符兩邊的操作數(shù)保留下來,然后讓它參與到下一步的計算中去。(好吧,這個對象也是臨時的,但它的代價非常非常小,因為它不需要動態(tài)申請內(nèi)存空間,我們先不理會它)。
class String;template <typename L> class ExpPlus {const L &lstr;const char *rstr;const int _size; public:ExpPlus(const L & a_l, const char *a_r):lstr(a_l), rstr(a_r), _size(strlen(a_r)){}ExpPlus(const L & a_l, const char &a_r):lstr(a_l), rstr(&a_r), _size(1){}ExpPlus(const L & a_l, const String &a_r):lstr(a_l), rstr(a_r.c_str()), _size(a_r.size()){}void copy_str(char *d_str) const;int size() const{return _size + lstr.size();} };template <typename L> void ExpPlus<L>::copy_str(char *d_str) const {lstr.copy_str(d_str);strncpy(d_str + lstr.size(), rstr, _size); } //用于+模板 template <typename L> ExpPlus<L> operator + (const L & a_l, const char & a_r) {return ExpPlus<L>(a_l, a_r); }template <typename L> ExpPlus<L> operator + (const L & a_l, const char* a_r) {return ExpPlus<L>(a_l, a_r); }template <typename L> ExpPlus<L> operator + (const L & a_l, const String & a_r) {return ExpPlus<L>(a_l, a_r); }我們增加了一個模板類 ExpPlus,用它來代表加法計算的“表達(dá)式”,但在進(jìn)行加法時,它本身并不進(jìn)行真正的計算。對這個類,定義了copy_str函數(shù),在這個函數(shù)中才進(jìn)行了真正的字符串加法計算。當(dāng)然,它除了支持保存String對象,還支持保存char和char *。因為String對象加字符串常量和字符也是很常見的,如下面的表達(dá)式:
String str2 = str1 + 'a' + "cd"
對于我們實現(xiàn)的String類,必須重新重載一個operator = 函數(shù),用于一次性計算右邊表達(dá)式的值。修改后的String代碼如下:
class String { public:。。。template <typename Exp>String& operator=(const Exp &a_r); //新加 。。。void copy_str(char *d_str) const; //新加 };void String::copy_str(char *d_str) const {strcpy(d_str, _string); }template <typename Exp> String& String::operator=(const Exp &a_r) {char *temp_str;int size = a_r.size();temp_str = new char[size + 1];a_r.copy_str(temp_str);temp_str[size] = 0;if (_string)delete _string;_string = temp_str;_size = size;return *this; }上面只給出了新加的代碼,其它代碼在自己實現(xiàn)簡單的string類中已經(jīng)給出。
上面這段話,對于不了解ET的人來說,也許一時間還不容易明白,我們一步一步來:
在 str4 = str1 + str2 + str3這個式子中,首先遇到 str1 + str2,這時,模板函數(shù) operator + 會被調(diào)用,這時只是生成一個臨時的ExpPlus<String>對象(我們叫它 t1 吧),不做計算,只是保留計算的左右操作數(shù)(也就是str1和str2),接著,t1 + str3 ,再次調(diào)用同樣的 operator + ,而且也只是生成一個對象(我們叫它 t2 吧),這個對象的類型是 ExpPlus<ExpPlus<String>>,同樣,t2 在這里只是保留了兩邊的操作數(shù)(也就是 t1 和 str3)。直到整個表達(dá)式“做完”,沒有任何東西進(jìn)行了計算,所做的事情實際上只是用 ExpPlus 這個模板類把計算式的信息記錄下來了(當(dāng)然,這些信息就是參與計算的操作數(shù))。
最后,當(dāng)進(jìn)行 str4 = t2 的時候,String的賦值運算符被調(diào)用(用 t2 作參數(shù))。注意,這個調(diào)用中的語句a_r.copy_str(temp_str),實際是是調(diào)用t2.copy_str(temp_str),t2.copy_str函數(shù)中又調(diào)用t1.copy_str,t1又調(diào)用str1.copy_str,就這樣一步一步地將str1,str2,str3中的字符串拷貝到temp_str中,最終得到str4 = str1 + str2 + str3。就像變“魔術(shù)”一樣,我們通過ExpPlus完成了“延遲計算”,并避免了大型的 String臨時對象的產(chǎn)生。
4.總結(jié)
表達(dá)式模板保持了表達(dá)式直觀和效率兩者,很強大,但很顯然它太復(fù)雜,主要是作為類庫的設(shè)計者的武器。另外,它也可能使得使用者要理解一些“新”東西,比如,如果我想存儲表達(dá)式的中間值,那么 <ExpPlus<ExpPlus<...<String>...> 一定會讓使用者理解半天。
?
參考:http://www.cnblogs.com/liyiwen/archive/2009/12/03/1616627.html
http://www.cppblog.com/kesalin/archive/2009/05/28/85983.html
總結(jié)
以上是生活随笔為你收集整理的Expression Template(表达式模板,ET)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Liskon替换原则
- 下一篇: 关于COCOS2D-X 中的音乐与音效应