2014-02-14 29 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

相关问题