Ответ 1
комментарии в строке, но кратко:
-
вы хотите, чтобы все назначения перемещения и перемещения конструкторов были
noexcept
, если это вообще возможно. Стандартная библиотека намного быстрее, если вы включите ее, поскольку она может исключить любую обработку исключений из алгоритмов, которые изменяют порядок последовательности вашего объекта. -
, если вы собираетесь определить пользовательский деструктор, сделайте это noexcept. Зачем открывать ящик пандоры?Я ошибся. По умолчанию это не исключено. -
В этом случае предоставление надежной гарантии исключений является безболезненным и почти ничего не стоит, поэтому сделаем это.
код:
#include <algorithm>
#include <cstddef>
class DumbArray {
public:
DumbArray(std::size_t size = 0)
: size_(size), array_(size_ ? new int[size_]() : nullptr) {
}
DumbArray(const DumbArray& that)
: size_(that.size_), array_(size_ ? new int[size_] : nullptr) {
std::copy(that.array_, that.array_ + size_, array_);
}
// the move constructor becomes the heart of all move operations.
// note that it is noexcept - this means our object will behave well
// when contained by a std:: container
DumbArray(DumbArray&& that) noexcept
: size_(that.size_)
, array_(that.array_)
{
that.size_ = 0;
that.array_ = nullptr;
}
// noexcept, otherwise all kinds of nasty things can happen
~DumbArray() // noexcept - this is implied.
{
delete [] array_;
}
// I see that you were doing by re-using the assignment operator
// for copy-assignment and move-assignment but unfortunately
// that was preventing us from making the move-assignment operator
// noexcept (see later)
DumbArray& operator=(const DumbArray& that)
{
// copy-swap idiom provides strong exception guarantee for no cost
DumbArray(that).swap(*this);
return *this;
}
// move-assignment is now noexcept (because move-constructor is noexcept
// and swap is noexcept) This makes vector manipulations of DumbArray
// many orders of magnitude faster than they would otherwise be
// (e.g. insert, partition, sort, etc)
DumbArray& operator=(DumbArray&& that) noexcept {
DumbArray(std::move(that)).swap(*this);
return *this;
}
// provide a noexcept swap. It the heart of all move and copy ops
// and again, providing it helps std containers and algorithms
// to be efficient. Standard idioms exist because they work.
void swap(DumbArray& that) noexcept {
std::swap(size_, that.size_);
std::swap(array_, that.array_);
}
private:
std::size_t size_;
int* array_;
};
В операторе переадресации можно сделать еще одно повышение производительности.
Решение, которое я предложил, обеспечивает гарантию того, что перемещенный массив будет пустым (при освобождении ресурсов). Возможно, это не то, что вы хотите. Например, если вы отслеживали объем и размер DumbArray отдельно (например, например, std::vector), тогда вам может понадобиться сохранить любую выделенную память в this
в that
после перемещения. Тогда это позволит that
присваиваться, возможно, уйти без выделения другой памяти.
Чтобы включить эту оптимизацию, мы просто реализуем оператор move-assign в терминах (noexcept) swap:
так из этого:
/// @pre that must be in a valid state
/// @post that is guaranteed to be empty() and not allocated()
///
DumbArray& operator=(DumbArray&& that) noexcept {
DumbArray(std::move(that)).swap(*this);
return *this;
}
:
/// @pre that must be in a valid state
/// @post that will be in an undefined but valid state
DumbArray& operator=(DumbArray&& that) noexcept {
swap(that);
return *this;
}
В случае с DumbArray, вероятно, стоит использовать более расслабленную форму на практике, но остерегайтесь тонких ошибок.
например.
DumbArray x = { .... };
do_something(std::move(x));
// here: we will get a segfault if we implement the fully destructive
// variant. The optimised variant *may* not crash, it may just do
// something_else with some previously-used data.
// depending on your application, this may be a security risk
something_else(x);