Как правильно применить правило 5 (или ноль?) К классу, содержащему вектор пользовательских объектов со строками

У меня проблемы с тем, чтобы сосредоточиться на владении и максимизировать производительность с помощью движений. Представьте себе этот гипотетический набор классов, эмулирующий книгу Excel.

namespace Excel {

class Cell
{
public:
  // ctors
  Cell() = default;
  Cell(std::string val) : m_val(val) {};
  // because I have a custom constructor, I assume I need to also
  // define copy constructors, move constructors, and a destructor.
  // If I don't my understanding is that the private string member 
  // will always be copied instead of moved when Cell is replicated 
  // (due to expansion of any vector in which it is stored)? Or will 
  // it be copied, anyways (so it doesn't matter or I could just 
  // define them as default)

  value() const { return m_val; }; // getter (no setter)
private:
  std::string m_val;
}

class Row
{
public:
  // ctors
  Row() = default;
  Row(int cellCountHint) : m_rowData(cellCountHint) {}

  // copy ctors (presumably defaults will copy private vector member)
  Row(const Row&) = default;
  Row& operator=(Row const&) = default;

  // move ctors (presumably defaults will move private vector member)
  Row(Row&& rhs) = default;
  Row& operator=(Row&& rhs) = default;

  // and if I want to append to internal vector, might I get performance
  // gains by moving in lieu of copying, since Cells contain strings of
  // arbitrary length/size?
  void append(Cell cell) { m_rowData.push_back(cell); };
  void append(Cell &&cell) { m_rowData.push_back(std::move(cell)); };
private:
  std::vector<Cell> m_rowData;
}

}

И так далее:

  • Класс Worksheet будет содержать вектор строк
  • Класс Workbook будет содержать вектор Worksheets

Я не чувствовал необходимости реализовывать последние два для MWE, так как они фактически дублируют Row (я предполагаю, что они будут идентичны дизайну Row).

Меня сбивает с толку понимание, можно ли полагаться на значения по умолчанию или я должен определять свои собственные конструкторы перемещения (а не сохранять их по умолчанию), чтобы гарантировать, что приватная векторная переменная-член перемещается, а не копируется, но это все очень сбивает с толку мне и мне кажется, что я могу только найти чрезмерно упрощенные примеры класса, в котором нет ничего, кроме членов встроенных типов.

1 ответ

Решение

Если класс имеет дело с владением ресурсом, то этот класс должен ТОЛЬКО управлять этим ресурсом. Это не должно делать ничего другого. В этом случае определите все 5 (правило 5).

Иначе, класс не должен реализовывать ни одного из 5 (правило 0).

Просто как тот.


Теперь я видел, как эти правила сформулированы следующим образом: если класс определяет любое из 5, то он должен определить все из них. Ну да и нет. Причиной этого является то, что: если класс определяет любой из 5, то это сильный индикатор того, что класс должен управлять ресурсом, и в этом случае он должен определить все 5. Так, например, если вы определяете деструктор для освобождения ресурс, то класс находится в первой категории и должен реализовывать все 5, но если вы определяете деструктор просто для добавления некоторых операторов отладки или для ведения журнала, или потому что класс полиморфен, тогда класс не первая категория, поэтому вы не не нужно определять все 5.

Если ваш класс относится ко второй категории, и вы определяете хотя бы одну из 5, то вам следует явно =default остальные 5. Это потому, что правила, когда cpy/move ctors/assignments неявно объявляются, немного сложны. Например, определение dtor предотвращает неявное объявление хода ctor & assigment


// because I have a custom constructor, I assume I need to also
// define copy constructors, move constructors, and a destructor.

Неправильно. Пользовательский конструктор не является частью 5.


Все ваши классы должны следовать правилу 0, поскольку ни один из них не управляет ресурсами.

На самом деле для пользовательского кода довольно редко нужно реализовывать правило 5 класса. Обычно это реализовано в библиотеке. Так что, как пользователь, почти всегда по правилу 0.


Которы / назначения по умолчанию для копирования / перемещения выполняют ожидаемое действие: копии копируют каждого члена, а перемещенные перемещают каждого члена. Что ж, при наличии элементов, которые не являются подвижными, или у которых есть только некоторые из 5, или у которых есть некоторые из 5 удаленных, тогда правила становятся немного более сложными, но нет неожиданного поведения. По умолчанию это хорошо.

Другие вопросы по тегам