2011-03-28 76 views
11

在書C++入門手冊中,它有一個C型字符數組的代碼,並說明如何在文章15.3運算符=中過載=運算符。現在運算符= C++中的重載

String& String::operator=(const char *sobj) 
{ 
    // sobj is the null pointer, 
    if (! sobj) { 
     _size = 0; 
     delete[] _string; 
     _string = 0; 
    } 
    else { 
     _size = strlen(sobj); 
     delete[] _string; 
     _string = new char[ _size + 1 ]; 
     strcpy(_string, sobj); 
    } 
    return *this; 
} 

我想知道爲什麼有需要返回參考String &時,下面這段代碼做相同的工作,沒有任何問題:

void String::operator=(const char *sobj) 
{ 
    // sobj is the null pointer, 
    if (! sobj) { 
     _size = 0; 
     delete[] _string; 
     _string = 0; 
    } 
    else { 
     _size = strlen(sobj); 
     delete[] _string; 
     _string = new char[ _size + 1 ]; 
     strcpy(_string, sobj); 
    } 

} 
  • 請大家幫幫忙。
+3

注意,在書中提出的解決方案是不例外安全(和這將會非常簡單,我不明白他們爲什麼沒有這樣做)。 – 2011-03-28 19:15:54

回答

20

它支持以下成語:

String a, b; 
const char *c; 

// set c to something interesting 

a = b = c; 

對於這項工作,b = c必須返回一個適當的對象或參考分配給a;根據C++運算符優先規則,它實際上是a = (b = c)

如果您要返回指針this,則必須編寫a = *(b = c),但不能表達意圖的含義。

+1

現在,這是一個單行 - 爲什麼你寫這本書。謝謝。得到了我的答案。 – Sadique 2011-03-28 18:38:09

+0

我必須等上9分鐘才能接受這個答案!!!!!! – Sadique 2011-03-28 18:41:36

+1

@Acme:沒錯,你必須等到其他人都有機會證明我錯了。 – 2011-03-28 18:44:18

0

從運營商返回=是,所以你可以鏈接的東西。看到這裏不附帶任何條件:

x = y = 2; 

該行的y=2一部分返回2.這是X如何得到的值。如果op =返回void,則不能鏈接=操作。

3

公約。內置類型做到這一點,自動生成的賦值運算符做到這一點,它可以讓你做到這一點:

a = b = c; 

或本:

if (foo = come_function()) { 
    // use foo here 
} 
+0

+1作爲條件賦值,我忘了那一個。簡而言之,這個慣例給了你C++程序員喜歡的所有槍瞄能力;) – 2011-03-28 18:46:21

+0

請注意,後者是不好的風格,應該避免。 – 2011-03-28 18:47:25

+0

請注意,不管後者是否是不好的風格都是前面討論過的味道問題。有時候我會習慣使用這個習語,而當我這樣做時,我仍然不會感到非常尷尬。雖然我不喜歡它,但我曾經這樣做過;這是一個老C程序員的習慣。 – 2011-03-28 18:49:21

0

按照慣例,你能做到如:

int x, y, z; 
x = y = z = 0; 

if ((x = 1) != 0) { ... } 

爲了保持這些語義的類的實例,你需要返回*this - 允許鏈式分配。如果你只返回this你會指向返回到您的實例,並鏈接分配是行不通的 - 有沒有operator=String *,有一個String

8

@larsmans已經回答了你確切的問題,所以我實際上會離題:這是一些蹩腳的代碼!

這裏的問題是3倍:

  1. 你剛纔複製的拷貝構造函數(有點)
  2. strcpy可以更好地通過strncpy取代,後者做了一些邊界檢查
  3. 的代碼這是不是例外安全

1)和2)更多styli stic比任何東西,但3)是一個大問題

編輯:正如@Jerry Coffin所指出的,這不能防止自我分配。這就是說,如果sobj_string指向相同的字符數組,則會遇到很大的麻煩。這篇文章最後的簡單解決方案也涵蓋了這種情況。

也不例外安全

讓我們看一下代碼,即一個部分,該部分else

_size = strlen(sobj); 
    delete[] _string; 
    _string = new char[ _size + 1 ]; 
    strcpy(_string, sobj); 

如果由於某種原因,new拋出,會發生什麼?

  • _size有新的字符串的值
  • _string指向舊的指針......已經釋放

因此不僅對象將處於不可用狀態(半來自新對象的數據,一半來自老),但它甚至不能被破壞(除非析構函數泄漏......?)

添加異常安全,硬盤的方式

_size = strlen(sobj); 
    delete[] _string; 

    try { 
    _string = new char[ _size + 1 ]; 
    } catch(...) { 
    _size = 0; _string = 0; 
    throw; 
    } 
    strcpy(_string, sobj); 

好,它是真正的底線,但它帶給我們的基本異常保證:沒有功能的保證,但保證該代碼是技術上是正確的(無碰撞,無泄漏)。

添加異常安全,最簡單的方法:複製和交換成語

找到一個更完整的描述:What is the copy-and-swap idiom ?

void swap(String& lhs, String& rhs) { 
    using std::swap; 
    swap(lhs._size, rhs._size); 
    swap(lhs._string, rhs._string); 
} 

String& String::operator=(String other) { // pass-by-value 
    swap(*this, other); 
    return *this; 
} 

它是如何工作的?

  • 我們重用的實際拷貝
  • 我們重用交換功能的交換價值的拷貝構造函數
  • 我們重新用於清理

析構函數和它比甚至更好以前的版本,現在我們有強烈的例外保證:它是事務性的,因此如果它失敗了,我們分配給的字符串是不變的(就像什麼都沒發生過一樣)。

More about Exception Guarantees

我有點氣餒,一個C++教程會促進這種可疑代碼,請告訴我它是什麼不該做的例子:/

+0

@Jerry:好點,我會編輯它。 – 2011-03-29 06:37:18