将不可复制的对象放入std容器中

Putting non-copyable objects into std-containers

本文关键字:std 可复制 对象      更新时间:2023-10-16

这个类是否设计为标准的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;
  }
};
你注意到,我在这里定义了新的movemove-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的想法只是一个猜测,在这里。

编辑:为了方便读者,我将答案放入示例代码中。

你的示例代码看起来大部分是正确的。

  1. const del>
  2. if(&other!=this)在你的移动赋值操作符看起来不必要,但无害。

  3. 初始化列表向量构造函数不能工作。这将尝试复制你的DataHolder,你应该得到一个编译时错误。

  4. 带右值参数的push_back和emplace_back调用可以工作。使用左值参数(使用y)会给你编译时错误。

push_back和emplace_back在使用方式上没有区别。emplace_back适用于不想在vector之外构造DataHolder,而是传递参数仅在vector内部构造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;
}

没有名称的临时对象,例如: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&&