Помещение не копируемых объектов в std-контейнеры - PullRequest
5 голосов
/ 16 апреля 2011

Является ли дизайн этого класса стандартным способом C ++ 0x для предотвращения копирования и присвоения для защиты клиентского кода от случайного двойного удаления data?

struct DataHolder {
  int *data;   // dangerous resource
  DataHolder(const char* fn); // load from file or so
  DataHolder(const char* fn, size_t len); // *from answers: added*
  ~DataHolder() { delete[] data; }

  // prevent copy, to prevent double-deletion
  DataHolder(const DataHolder&) = delete;
  DataHolder& operator=(const DataHolder&) = delete;

  // enable stealing
  DataHolder(DataHolder &&other) {
    data=other.data; other.data=nullptr;
  }
  DataHolder& operator=(DataHolder &&other) {
    if(&other!=this) { data = other.data; other.data=nullptr};
    return *this;
  }
};

Вы заметили, что я определил новые методы move и move-assign .Правильно ли я их реализовал?

Могу ли я - с помощью определений move и move-assign - поместить DataHolder в стандарт?контейнер?как vector?Как мне это сделать?

Интересно, некоторые варианты приходят на ум:

// init-list. do they copy? or do they move?
// *from answers: compile-error, init-list is const, can nor move from there*
vector<DataHolder> abc { DataHolder("a"), DataHolder("b"), DataHolder("c") };

// pushing temp-objects.
vector<DataHolder> xyz;
xyz.push_back( DataHolder("x") );
// *from answers: emplace uses perfect argument forwarding*
xyz.emplace_back( "z", 1 );

// pushing a regular object, probably copies, right?
DataHolder y("y");
xyz.push_back( y ); // *from anwers: this copies, thus compile error.*

// pushing a regular object, explicit stealing?
xyz.push_back( move(y) );

// or is this what emplace is for?
xyz.emplace_back( y ); // *from answers: works, but nonsense here*

Идея emplace_back здесь только предположение.

Редактировать : я обработал ответы в примере кода, для удобства читателей.

Ответы [ 2 ]

6 голосов
/ 16 апреля 2011

Ваш пример кода выглядит в основном правильно.

  1. const DataHolder &&other (в двух местах).

  2. if(&other!=this) в вашем операторе присваивания перемещений выглядит ненужным, но безвредным.

  3. Конструктор вектора списка инициализатора не будет работать.Это попытается скопировать ваш DataHolder, и вы получите ошибку времени компиляции.

  4. Вызовы push_back и emplace_back с аргументами rvalue будут работать.Те, у которых есть аргументы lvalue (используя y), приведут к ошибкам времени компиляции.

На самом деле нет никакой разницы между push_back и emplace_back в том, как вы их использовали.emplace_back предназначен для случаев, когда вы не хотите создавать объект DataHolder вне вектора, а вместо этого передаете аргументы для создания объекта только внутри вектора.Например:

// Imagine this new constructor:
DataHolder(const char* fn, size_t len);

xyz.emplace_back( "data", 4 );  // ok
xyz.push_back("data", 4 );  // compile time error

Обновление:

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

DataHolder& operator=(DataHolder &&other)
{
   if(&other!=this)
   {
      delete[] data;  // insert this
      data = other.data;
      other.data=nullptr;
   }
   return *this;
}
1 голос
/ 16 апреля 2011

Временные объекты, которые не имеют имени, например.DataHolder("a"), перемещаются при наличии.Стандартный контейнер в C ++ 0x всегда будет перемещаться, когда это возможно, это также позволяет помещать std::unique_ptr в стандартный контейнер.
Кроме того, вы неправильно реализовали свои операции перемещения:

  // enable stealing
  DataHolder(const DataHolder &&other) {
    data=other.data; other.data=nullptr;
  }
  DataHolder& operator=(const DataHolder&&other) {
    if(&other!=this) { data = other.data; other.data=nullptr};
    return *this;
  }

Как вы движетесь от постоянного объекта?Вы не можете просто изменить other data, так как other является константой.Измените это на простой DataHolder&&.

...