class A
{
public:
A() noexcept
: _buf(new char[128])
{}
In the above, A()
will call std::terminate()
if new char[128]
throws an exception.
~A() noexcept
{
if (_buf)
{
delete[] _buf;
_buf = nullptr;
}
}
In the above, looks ok. Could be simplified down to:
~A() noexcept
{
delete[] _buf;
}
A(const A& other) noexcept
: A()
{
for (int i = 0; i < 128; ++i)
{
_buf[i] = other._buf[i];
}
}
In the above, will call std::terminate()
if new char[128]
throws an exception. But otherwise fine.
A(A&& other) noexcept
: _buf(other._buf)
{
_buf = nullptr;
}
In the above, looks good.
A& operator =(const A& other) noexcept
{
if (this != &other)
{
this->~A();
new(this) A(other);
}
return *this;
}
In the above, normally I'd say this is dangerous. What if new(this) A(other);
throws? In this case, it won't, because if it tries to, the program will terminate. Whether that is safe behavior or not depends on the application (terminate didn't work well for Ariane 5, but works fine in more mundane applications).
A& operator =(A&& other) noexcept
{
if (this != &other)
{
this->~A();
new(this) A(static_cast<A&&>(other));
}
return *this;
}
The above should work fine. Though I'm not sure it is superior to this non-branching version below with otherwise equivalent performance. A behavioral difference is that the version below is not a no-op for self-move-assignment. However it is my belief that self-move-assignment need not be a no-op as one of the post conditions state that the resulting value is unspecified (the other post-condition states that it is specified, leading to a undependable contradiction).
A& operator =(A&& other) noexcept
{
delete [] _buf;
_buf = nullptr;
_buf = other._buf;
other._buf = nullptr;
return *this;
}