Можно ли безопасно использовать векторы копирования / перемещения для реализации операторов назначения копирования / перемещения?

Я думаю, что следующий код лучше, чем идиома копирования и замены.

Таким образом, вы можете использовать два макроса для инкапсуляции определений оператора назначения копирования и перемещения. Другими словами, вы можете избежать явного определения их в своем коде. Как следствие, вы можете сосредоточить свое внимание только на 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;
};

0

Решение

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;
}
2

Другие решения

Он будет работать правильно в предоставленном вами контексте.

Эта техника будет катастрофической, когда 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, Я лично удалил бы noexcept от A() а также A(const A&), но если вы хотите, чтобы программа terminate на неудачу распределения это ваш выбор;)

Если ваша цель состоит в том, чтобы просто избежать написания операторов присваивания — и я не виню вас, они досадно банальны — вы должны разработать Правило нуля:

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

A() : _buf(bufsize) {}

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

Там — все скрытые копии и ходы.

1
По вопросам рекламы [email protected]