在不释放内存的情况下销毁std :: vector

时间:2014-10-23 09:43:36

标签: c++ c++11 vector std stdvector

假设我有一个将数据导入std向量的函数:

void getData(std::vector<int> &toBeFilled) {
  // Push data into "toBeFilled"
}

现在我想将这些数据发送到另一个函数,该函数应在完成时释放数据:

void useData(int* data)
{
  // Do something with the data...
  delete[] data;
}

两个函数(getData和useData)都是固定的,无法更改。复制数据一次就可以正常工作:

{
  std::vector<int> data;
  getData(data);
  int *heapData = new int[data.size()];
  memcpy(heapData, data.data(), data.size()*sizeof(int));
  useData(heapData);
  data.clear();
}

但是,这个memcpy操作很昂贵而且并不是真的需要,因为数据已经在堆上了。是否可以直接提取和使用std向量分配的数据?像(伪代码)的东西:

{
  std::vector<int> data;
  getData(data);
  useData(data.data());
  data.clearNoDelete();
}

修改

这个例子可能没有多大意义,因为在函数调用useData之后可以释放向量。但是,在实际代码中,useData不是一个函数,而是一个接收数据的类,这个类的寿命比向量长...

2 个答案:

答案 0 :(得分:25)

您正在使用的API有一份合同,声明它拥有您提供的数据的所有权,并且该数据是通过指针提供的。这基本上排除了使用标准向量。

Vector将始终确保释放它分配的内存并安全地销毁它包含的元素。这是其保证合同的一部分,你无法拒绝。

如果您希望取得数据的所有权, 可以复制数据...或者每个元素移出到您自己的容器中。或者首先从你自己的new[]开始(唉)尽管你至少可以将这一切包含在模仿std::vector并变为非拥有的某个类中。

答案 1 :(得分:7)

这是一个可怕的黑客,应该允许你做你需要的,但它依赖于Undefined Behavior做最简单的事情。我们的想法是创建一个与std::allocator布局兼容的自己的分配器,并对向量进行输入:

template <class T>
struct CheatingAllocator : std::allocator<T>
{
  using typename std::allocator<T>::pointer;
  using typename std::allocator<T>::size_type;

  void deallocate(pointer p, size_type n) { /* no-op */ }

  // Do not add ANY data members!!
};


{
  std::vector<int, CheatingAllocator<int>> data;
  getData(reinterpret_cast<std::vector<int>&>(data)); // type pun, `getData()` will use std::allocator internally
  useData(data.data());
  // data actually uses your own allocator, so it will not deallocate anything
}

请注意,它像hacky一样黑客和不安全。它依赖于内存布局不变,它依赖于std::allocatornew[]函数内使用allocate。我自己也不会在生产代码中使用它,但我相信它是一个(绝望的)解决方案。


@TonyD在评论中正确地指出std::allocator很可能在内部使用new[]。因此,上述内容很可能会在delete[] useData()内失败。同样的@TonyD也很好地使用reserve()来(希望)阻止getData()内的重新分配。所以更新后的代码如下所示:

template <class T>
struct CheatingAllocator : std::allocator<T>
{
  using typename std::allocator<T>::pointer;
  using typename std::allocator<T>::size_type;

  pointer allocate(size_type n) { return new T[n]; }

  void deallocate(pointer p, size_type n) { /* no-op */ }

  // Do not add ANY data members!!
};


{
  std::vector<int, CheatingAllocator<int>> data;
  data.reserve(value_such_that_getData_will_not_need_to_reallocate);
  getData(reinterpret_cast<std::vector<int>&>(data)); // type pun, `getData()` will use std::allocator internally
  useData(data.data());
  // data actually uses your own allocator, so it will not deallocate anything
}