C++ 中重載 + 操作符的正確方法
作者:Danny Kalev
編譯:MTT 工作室
原文出處:Overloading Operator + the Right Way
摘要:本文概要性地介紹如何選擇正確的策略來為用戶定義類型重載 + 操作符。 |
int x=4+2;
內(nèi)建的 + 操作符有兩個類型相同的操作數(shù),相加并返回右值 6,然后被賦值給 x。我們可以斷定內(nèi)建的 + 是一個二元的,對稱的,可交換的操作符。它產(chǎn)生的結(jié)果的類型與其操作數(shù)類型相同。按照這個規(guī)測,當(dāng)你為某個用戶定義類型重載操作符時,也應(yīng)該遵循相應(yīng)內(nèi)建操作符的特征。
為用戶定義類型重載 + 操作符是很常見的編程任務(wù)。盡管 C++ 提供了幾種實現(xiàn)方法,但是它們?nèi)菀资谷水a(chǎn)生設(shè)計上的誤解,這種誤解常常影響代碼的正確性,性能以及與標(biāo)準庫組件之間的兼容性。
下面我們就來分析內(nèi)建操作符的特征并嘗試模仿其相應(yīng)的重載機制。
第一步:在成員函數(shù)和非成員函數(shù)之間選擇
你可以用類成員函數(shù)的方式實現(xiàn)二元操作符如:+、- 以及 ==,例如:
class String{public: bool operator==(const String & s); // 比較 *this 和 s};
這個方法是有問題的。相對于其內(nèi)建的操作符來說,重載的操作符在這里不具有對稱性;它的兩個參數(shù)一個類型為:const String * const(這個參數(shù)是隱含的),另一個類型為:const String &。因此,一些 STL 算法和容器將無法正確處理這樣的對象。
另外一個可選方法是把重載操作符 + 定義為一個外部(extern)函數(shù),該函數(shù)帶兩個類型相同的參數(shù):
String operator + (const String & s1, const String s2);
這樣一來,類 String 必須將該重載操作符聲明為友元:
class String{public: friend String operator+(const String& s1,const String&s2);};
第二步:返回值的兩難選擇
如前所述,內(nèi)建操作符 + 返回右值,其類型與操作數(shù)相同。但是在調(diào)用者堆棧里返回一個對象效率很低,處理大型對象時尤其如此。那么能不能返回一個指針或引用呢?答案是不行。因為返回指針破壞參數(shù)類型與返回值類型應(yīng)該相同的規(guī)則。更糟的是,鏈接多個表達式將成為不可能:
String s1,s2,s3;String res;res=s1+s2+s3; // 不可能用 String* 作為返回值
雖然有一個辦法可以定義額外的 + 操作符重載版本,但這個辦法是我們不希望用的,因為返回的指針必須指向動態(tài)分配的對象。這樣的話,如果調(diào)用者釋放(delete)返回的指針失敗,那么將導(dǎo)致內(nèi)存泄漏。顯然,返回 String* 不是一個好主意。
那么返回 String& 好不好呢?返回的引用必須一定要是一個有效的 String。它避免了使用動態(tài)對象分配,該方法返回的是一個本地靜態(tài)對象的引用。靜態(tài)對象確實解決了內(nèi)存泄漏問題,但這個方法的可行性仍然值得懷疑。在一個多線程應(yīng)用中,兩個線程可能會并發(fā)調(diào)用 + 操作符,因此造成 String 對象的混亂。而且,因為靜態(tài)對象總是保留其調(diào)用前的狀態(tài),所以有必要針對每次 + 操作符的調(diào)用都清除該靜態(tài) String 對象。由此看來,在堆棧上返回結(jié)果仍然是最安全和最簡單的解決方案。