正確的重載operator+

  爲了得到一串鏈表或者完成兩個對象的累加或合併,經常要重載諸如string,date,complex或file等user-define類型的Operator+。然而,就設計而言,正確地重載operator+面臨許多挑戰。下面的段落中,我將介紹爲用戶定義類型選擇正確的重載operator+的一般性的策略。
    考慮下面的表達式:
    int x=4+2;
    內建的+運算符把兩個類型相同的操作數相加然後返回右值6傳遞給x,因此我們可以說:內建的運算符’+‘是基於二進制的、對稱的並且是可交換的操作符,它返回一個與操作數具有相同類型的值。這是一個規則,當你爲user-defined類型重載一個運算符時,應該維持相應內建運算符的特性。
 
    爲user-defined類型重載operator+是個很普通的事情。不過,由於C++提供了多種實現方式,導致在設計時很容易造成錯誤,從而對利用標準庫組件寫出的代碼的正確性、執行性能和兼容性造成不利影響。
 
    分析並內建操作符的特性去重載與之相應的user-define運算符。
    第一步:選擇成員函數或是非成員函數?
    我們可以象使用類的成員函數一樣使用+、-或是==等二進制操作符,例如:
    class String
    {
    public:
     bool operator==(const String & s); // compare *this and s
    };
    然而,這種實現方式值得懷疑。在這種情況下,作爲內建操作符的副本,重載後的操作符違背了內建操作符具有的對稱性,它的兩個操作符的類型分別是'const String* const'(隱含的this 參數)和'const String &',這將使得一些STL運算和某一些容器不能正確的處理這些對象。
    另一種反其道而行之的方法是,把重載的operator+定義爲外部函數,兩個參數的類型相同,如下:
    String operator + (const String & s1, const String s2);
    在這種情況下,類String要定義運算符的重載函數爲友元函數:
    class String
    {
    public:
     friend String operator+(const String& s1,const String&s2);
    };
    第二步:返回值,進退兩難的選擇
    正如前面所述,內建的operator+返回同它的操作符類型相同的右值。不過在caller的堆棧中運行對象將是低效率的,特別是在處理大對象的時候。能不能用返回一個指針或是一個傳引用來代替?在這裏不行。我們要求變量和返回值的類型要相同,返回指針破壞了這一特性。更糟糕的是,這使得我們不能使用多重連續表達式:
    String s1,s2,s3;
    String res;
    res=s1+s2+s3; //返回不合法的String*
    儘管有一個解決辦法,也就是另外定義operator+的不同的重載函數, 但這個解決方案沒有考慮到另一種情況:當返回的指針指向的是一個動態分配的對象,如果調用者沒有清除這個返回的指針,就會導致內存泄漏。所以很明顯的,返回String*是個糟糕的想法。
    那麼可不可以返回String&?同樣的,返回的引用必須綁定到一個合法的String。接下來,既然動態分配對象不行,那麼第二次我們何不選擇返回一個對局部靜態變量的引用?是的,靜態對象可以解決內存泄漏的問題。不過這仍然有些不可靠。比如在一個多線程程序中,兩個線程可能同時調用operator+,這就會導致對string對象的誤用。此外,因爲靜態對象保留上一次調用時的狀態,所以我們必須在每次調用operator+前清空這個靜態string對象。這樣我們得到結論,返回堆棧中的值仍然是最安全並且最簡單的解決方案。
    第三步:實現
    到目前爲止一切順利。在前面我們採用了外部友元函數並且採用by value返回值方式。那麼現在該是我們實現重載opertor+函數的時候了。然而又一次,我們面臨兩種選擇:
    選擇一:Intrusive Concatenation模型
    在Intrusive Concatenation模型中,operator+測量函數參數的長度,複製字符串到一個足夠地大的緩衝區並且返回結果 ( 爲簡潔的緣故,我忽略了不常見的情況例如堆過載):
    // Option 1: intrusive concatenation
    String operator + (const String & s1, const String s2)
    {
     int len=s1.size()+s2.size();
     String result;
     result.buf=new char[len+1];
     strcpy(result.buf, s1.buf);
     strcat(result.buf, s2.buf); // concatenate second string
     return result;
    }
    這個實現能夠較好的工作,不過還有更簡潔更靈活的基於重載的operator+=的解決方案。
    選擇二:使用運算符+=
    我們使用operator+=來,從而避免對private數據的存取、內存分配以及c函數的使用。
    // Option 2: using operator +=
    String operator + (const String & s1, const String s2)
    {
     String result=s1;
     result+=s2;
     return result;
    }
    這個實現方法比較容易,而且不會存取string類的非公有數據。並且,我們甚至不需要把它聲明爲友元函數。operator+=的重載實現留給讀者作爲練習。
    作爲重載operator+運算符的最廣泛的例子,我們討論的焦點集中在了自己編寫的一個string類。當然,你可以把我介紹的方法和策略應用到任意的user-defined類型上。正確的重載operator+基於以下原則:
    ●運算符對稱性
    ●返回值與操作符類型相同
    ●採用by value返回方式
    ●採用基於重載+=運算符的實行方式
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章