假设我有一个将数据导入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不是一个函数,而是一个接收数据的类,这个类的寿命比向量长...
答案 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::allocator
在new[]
函数内使用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
}