2016-10-13 34 views
1

我想實現一個類的拷貝構造函數和賦值操作符。我對複製交換習語有些困惑。特別是當涉及到複製構造函數時。拷貝交換習語與拷貝構造函數有什麼關係?我如何避免代碼重複?如何實現拷貝交換成語的複製構造函數

這裏是我的類

頁眉:

Class Actor 
{ 
public: 
    Foo* foo; 
    Bar bar; 
    double member1; 
    bool member2; 
    unsigned int member3; 

    void Swap(Actor& first, Actor& second); 
    Actor(const Actor&); 
    Actor& operator=(const Actor); 
} 

.cpp的:

void Actor::Swap(Actor& first, Actor& second) 
{ 
    // Swap wont work with my non pointer class 
    Bar temp = first.bar; 
    first.bar = second.bar; 
    second.bar = temp; 

    std::swap(first.foo, second.foo); 
    std::swap(first.member2, second.member2); 
    std::swap(first.member3, second.member3); 
} 

// What goes here? Is this a correct copy constructor? Does this have anything to do with a copy swap idiom? How can I avoid code duplication in my copy constructor? 
Actor::Actor(const Actor& other) 
{ 
    foo = new Foo(); 
    *foo = *other.foo; 

    bar = other.bar; 
    member1 = other.member1; 
    member2 = other.member2; 
    member3 = other.member3; 
} 

Actor& Actor::operator=(Actor other) 
{ 
    Swap(*this, other); 
    return *this; 
} 

我這個指南:What is the copy-and-swap idiom?

+0

什麼是'Record :: Actor'? 「Record ::' – StoryTeller

+0

沒有其他限定條件這是一個錯字。 – marsh

+2

除了將'Swap'聲明爲非靜態成員函數(而不是朋友函數或至少一個靜態成員函數)之外,這個習語似乎正確應用。你寫了副本和交換,並獲得了免費的任務(請注意,通過按值接收其參數來隱式調用複製構造函數)。另一個提示:調用它'swap'而不是'Swap'並使用'使用std :: swap; swap(x,y);'而不是'std :: swap(x,y)'啓用ADL,這對自定義類型很有用。 –

回答

5

如何避免代碼重複?

事實上,複製和交換(CAS)背後的主要動機不在於避免代碼重複,更多在於提供強大的例外保證。這意味着如果您嘗試執行復制分配並且失敗,那麼您嘗試分配的對象沒有以任何方式進行修改。一般來說,移動構造/賦值不會拋出,並且複製構造隱式地給出了這個保證,因爲如果引發異常而不是對象不存在。因此,在典型情況下,複製分配是「不合格的人」,不提供強有力的例外保證或更好的保證。

這就是說,強大的異常保證通常很難提供,並且在實踐中可能沒有那麼有用。我不會鼓勵人們自動假定CAS是最好的。

如果您的目標是避免代碼重複,最好的方法是使用零規則:http://en.cppreference.com/w/cpp/language/rule_of_three。基本上這個想法是爲你的類選擇單獨的成員,以便編譯器生成的複製/移動構造函數/賦值是你想要的行爲。我明白這個代碼可能是一個練習來獲得對這些功能的理解,但是當你爲了學習而編寫代碼時,應該意識到這一點。

編輯:最後的筆記。假設你使用的是C++ 11,一般來說推薦使用CAS的方法實際上是而不是是寫自己交換。相反,你會寫移動構造函數(不管怎麼說),然後移動賦值。從這兩個功能中,通用std::swap將能夠有效地交換你的課程。然後你可以在你的CAS實現中使用通用的swap。在某些情況下,你可能會寫自己的交換,但通常沒有必要。更詳細的討論在這裏:http://scottmeyers.blogspot.com/2014/06/the-drawbacks-of-implementing-move.html

4

副本交換方法是一種方法,根據重用交換和複製常量來實現複製賦值運算符以減少重複代碼並增加異常安全性。

不過我注意到了有關你的代碼的一些事情:

  • 您沒有析構函數,所以你會泄漏你的Foo* foo;對象時,你Actor s的破壞。
  • 在您的複製構造函數中,您可以一步分配和複製Foo,而無需分配給您指定的中間默認構造Foo
  • 您的Swap函數是一個接受兩個參數的成員,這意味着它有三個參數。它應該是單個參數成員,並與this或雙參數非成員交換。
  • 爲了與標準庫保持一致,我建議您調用您的交換功能swap而不是Swap,無論採用哪種實現方式。
  • 在您的交換中,寧願刪除Bar交換到Bar,而不是在Actor類中實現它:換句話說,讓Bar知道如何交換自己並簡單地利用該功能。
+0

應該一直使用copy swap嗎? – 0x499602D2

1

複製和交換分配成語需要兩個機制:副本c'tor,並且不拋出交換功能。
所以你的代碼跟着它的信。

這個想法是嘗試先做不安全的操作,然後通過定義一個新的對象來創建一個副本。如果失敗並拋出,則分配的對象保持不變,如果發生錯誤,則狀態是可預測的。這是例外的安全保證。

如果副本上沒有發生錯誤,則進行交換以完成分配。由於交換方法是不拋出的,因此不會使分配給對象處於不一致狀態。

最後,臨時的析構函數清除了所有留下的資源,因爲swap方法將這些資源交給它。

1

我想,這是實現好,除了幾件事情:

1)你忘了你的自定義Swap方法交換member1

2)複製交換習慣用法通常爲operator=提供強有力的例外保證。這意味着它或者在不更改對象或成功執行分配的情況下發生異常。在你的情況下,強大的異常安全是有問題的,因爲Bar賦值可能會引發異常。