2014-02-14 55 views
0

我認爲下面的代碼比copy-and-swap idiom更好。可以安全地使用複製/移動ctors來實現複製/移動賦值操作符嗎?

通過這種方式,你可以用兩個宏來封裝副本的定義和移動賦值運算符。換句話說,您可以避免在代碼中明確定義它們。因此,您只能將注意力集中在ctors和dtor上。

是否有方法的任何不利?

class A 
{ 
public: 
    A() noexcept 
     : _buf(new char[128]) 
    {} 

    ~A() noexcept 
    { 
     if (_buf) 
     { 
      delete[] _buf; 
      _buf = nullptr; 
     } 
    } 

    A(const A& other) noexcept 
     : A() 
    { 
     for (int i = 0; i < 128; ++i) 
     { 
      _buf[i] = other._buf[i]; 
     } 
    } 

    A(A&& other) noexcept 
     : _buf(other._buf) 
    { 
     _buf = nullptr; 
    } 

    A& operator =(const A& other) noexcept 
    { 
     if (this != &other) 
     { 
      this->~A(); 
      new(this) A(other); 
     } 

     return *this; 
    } 

    A& operator =(A&& other) noexcept 
    { 
     if (this != &other) 
     { 
      this->~A(); 
      new(this) A(static_cast<A&&>(other)); 
     } 

     return *this; 
    } 

private: 
    char* _buf; 
}; 
+1

你的默認構造函數'noexcept'究竟如何?而你的移動構造函數不會移動任何東西。在刪除某物之前,不需要檢查'nullptr'。爲什麼'static_cast '而不是'std :: move'? – Praetorian

+0

_buf(other._buf)等._buf = nullptr;只是移動操作。 – xmllmx

+0

'static_cast '可用於某些標準庫無法使用的環境。所以它比'std :: move'更加兼容。 – xmllmx

回答

2
class A 
{ 
public: 
    A() noexcept 
     : _buf(new char[128]) 
    {} 

在上文中,A()將調用std::terminate()如果new char[128]拋出異常。

~A() noexcept 
    { 
     if (_buf) 
     { 
      delete[] _buf; 
      _buf = nullptr; 
     } 
    } 

在上面,看起來沒問題。可以簡化到:

~A() noexcept 
    { 
     delete[] _buf; 
    } 



    A(const A& other) noexcept 
     : A() 
    { 
     for (int i = 0; i < 128; ++i) 
     { 
      _buf[i] = other._buf[i]; 
     } 
    } 

在上面,將調用std::terminate()如果new char[128]拋出異常。但否則罰款。

A(A&& other) noexcept 
     : _buf(other._buf) 
    { 
     _buf = nullptr; 
    } 

在上面,看起來不錯。

A& operator =(const A& other) noexcept 
    { 
     if (this != &other) 
     { 
      this->~A(); 
      new(this) A(other); 
     } 

     return *this; 
    } 

在上面,通常我會說這是危險的。如果new(this) A(other);拋出?在這種情況下,它不會,因爲如果它試圖,程序將終止。這是否是安全行爲取決於應用程序(終止並不適用於Ariane 5,但在更普通的應用程序中工作正常)。

A& operator =(A&& other) noexcept 
    { 
     if (this != &other) 
     { 
      this->~A(); 
      new(this) A(static_cast<A&&>(other)); 
     } 

     return *this; 
    } 

以上應該可以正常工作。雖然我不確定它是否優於下面的這個非分支版本以及其他相同的性能。行爲差異在於以下版本不適用於自動分配。然而,我相信自動分配不必是無操作的,因爲其中一個後置條件表明結果值沒有指定(另一個後置條件表明它被指定,導致一個不可靠的矛盾)。

A& operator =(A&& other) noexcept 
    { 
     delete [] _buf; 
     _buf = nullptr; 
     _buf = other._buf; 
     other._buf = nullptr; 
     return *this; 
    } 
1

它會在您提供的上下文中正常工作。

這種技術將是災難性的,當A是一個多態類和具有虛擬析構函數。

+0

你能舉個例子嗎? – xmllmx

+1

想象一下,A是一個「非最終」的數據類,比如一個保持筆刷和顏色的形狀。而來自A的B類是專門的形狀(例如圓形)。然後代碼'B b(...);'和後面的'b = A(...)'用於刷「b」的顏色,而不是調用它的析構函數(因爲析構函數是_virtual_函數)。更糟的是,'new(this)A'這行會將B的虛擬表指針替換爲A的虛擬表指針,這會導致難以發現的錯誤。 –

1

你可以大大使用unique_ptr<char[]>_buf簡化這一類:

class A 
{ 
public: 
    static const std::size_t bufsize = 128; 

    A() noexcept 
     : _buf(new char[bufsize]) 
    {} 

    A(const A& other) noexcept 
     : A() 
    { 
     copy_from(other); 
    } 

    A(A&& other) noexcept = default; 

    A& operator =(const A& other) noexcept 
    { 
     copy_from(other); 
     return *this; 
    } 

    A& operator =(A&& other) noexcept = default; 

private: 
    void copy_from(const A& other) noexcept { 
     std::copy_n(other._buf.get(), bufsize, _buf.get()); 
    } 

    std::unique_ptr<char[]> _buf; 
}; 

類是在未來變化的臉更短,更地道,更安全,因爲它避免了「巧「delete +展示位置new。我個人會刪除A()A(const A&)noexcept,但如果你想程序terminate上分配失敗那是你的選擇;)

如果你的目標僅僅是爲了避免寫賦值運算符 - 我不怪你,他們是煩人平庸 - 你應該設計到Rule of Zero

class A 
{ 
public: 
    static const std::size_t bufsize = 128; 

    A() : _buf(bufsize) {} 

private: 
    std::vector<char> _buf; 
}; 

有 - 所有隱式拷貝和移動。

+0

+1這是正確的方式來做到這一點,第二版應該是首選,除非你絕對無法承受'矢量'將花費你的少量額外的存儲字節。 – Praetorian

相關問題