我认为下面的代码比 copy-and-swap 习惯用法要好。
这样就可以用两个宏来封装复制和移动赋值运算符的定义。换句话说,您可以避免在代码中显式定义它们。因此,您可以仅将注意力集中在 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;
};
最佳答案
class A
{
public:
A() noexcept
: _buf(new char[128])
{}
在上面,如果 new char[128]
抛出异常,A()
将调用 std::terminate()
。
~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];
}
}
在上面,如果 new char[128]
抛出异常,将调用 std::terminate()
。但除此之外还好。
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;
}
以上应该可以正常工作。虽然我不确定它是否优于下面的非分支版本,但性能相当。行为差异是以下版本不是 self 移动分配的空操作。然而,我相信自移动分配不必是空操作,因为后置条件之一声明结果值未指定(另一个后置条件声明它已指定,导致不可靠的矛盾)。
A& operator =(A&& other) noexcept
{
delete [] _buf;
_buf = nullptr;
_buf = other._buf;
other._buf = nullptr;
return *this;
}
关于c++ - 可以安全地使用复制/移动构造函数来实现复制/移动赋值运算符吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21768878/