C++ 从std::vector接管内存

C++ 从std::vector接管内存,c++,c++11,stdvector,allocator,C++,C++11,Stdvector,Allocator,我使用一个外部库,它处理大量数据。数据由原始指针加上长度传入。库不声明指针的所有权,但在处理数据时调用提供的回调函数(使用相同的两个参数) 使用std::vector可以方便地准备数据,我不想放弃这种便利。复制数据是完全不可能的。因此,我需要一种方法来“接管”std::vector所拥有的内存缓冲区,并(稍后)在回调中释放它 我当前的解决方案如下所示: std::vector<T> input = prepare_input(); T * data = input.data(); s

我使用一个外部库,它处理大量数据。数据由原始指针加上长度传入。库不声明指针的所有权,但在处理数据时调用提供的回调函数(使用相同的两个参数)

使用
std::vector
可以方便地准备数据,我不想放弃这种便利。复制数据是完全不可能的。因此,我需要一种方法来“接管”std::vector所拥有的内存缓冲区,并(稍后)在回调中释放它

我当前的解决方案如下所示:

std::vector<T> input = prepare_input();
T * data = input.data();
size_t size = input.size();
// move the vector to "raw" storage, to prevent deallocation
alignas(std::vector<T>) char temp[sizeof(std::vector<T>)];
new (temp) std::vector<T>(std::move(input));
// invoke the library
lib::startProcesing(data, size);
std::vector input=prepare_input();
T*data=input.data();
size_t size=input.size();
//将向量移动到“原始”存储,以防止释放
alignas(std::vector)字符温度[sizeof(std::vector)];
新(临时)标准::向量(标准::移动(输入));
//调用库
lib::startProcessing(数据、大小);
在回调函数中:

void callback(T * data, size_t size) {
    std::allocator<T>().deallocate(data, size);
}
void回调(T*data,size\u T size){
std::allocator().deallocate(数据、大小);
}
这个解决方案是有效的,因为标准分配器的
deallocate
函数忽略了它的第二个参数(元素计数),只调用
::operator delete(data)
。如果不这样做,可能会发生不好的事情,因为输入向量的
大小
可能比其
容量
小很多


我的问题是:是否有一个可靠的(WRT?C++标准)接管代码的方法:STD::向量< /代码>,在以后的某个时间手动释放它?

< P>你不能从向量中获得内存的所有权,但是你可以用另一种方式来解决你的潜在问题。 下面是我的方法-由于静态全局变量而不是线程安全的,这有点不方便,但是可以通过对
注册表
对象的简单锁定来实现

static std::map<T*, std::vector<T>*> registry;
void my_startProcessing(std::vector<T> * data) {
  registry.put(data->data(), data);
  lib::startProcesing(data->data(), data->size());
}

void my_callback(T * data, size_t length) {
  std::vector<T> * original = registry.get(data);
  delete original;
  registry.remove(data);
}
静态std::map注册表;
作废我的\u开始处理(std::vector*数据){
registry.put(data->data(),data);
lib::startProcessing(数据->数据(),数据->大小());
}
作废my_回调(T*数据,大小\u T长度){
std::vector*original=registry.get(数据);
删除原件;
删除(数据);
}
现在你可以做了

std::vector<T> * input = ...
my_startProcessing(input);
std::vector*输入=。。。
my_启动处理(输入);
但要小心如果在调用
my_startProcessing
后向输入中添加/删除元素,则会发生不好的事情-库中的缓冲区可能会失效。(可能允许您更改向量中的值,因为我相信这将正确地写入to数据,但这也取决于库允许的内容。)


如果
t
=
bool
,这也不起作用,因为
std::vector::data()
不起作用。

您可以在向量上创建自定义类构建

这里的关键点是在
SomeData
构造函数中使用移动语义

  • 您在不复制的情况下获取准备好的数据(请注意,源向量将被清除)
  • 数据将由
    thisData
    向量析构函数正确处理
  • 源向量可以毫无问题地处理
由于基础数据类型将是数组,您可以计算起始指针和数据大小(请参见下面的
SomeDataImpl.h
):

SomeData.h

#pragma once
#include <vector>

template<typename T>
class SomeData
{
    std::vector<T> thisData;

public:
    SomeData(std::vector<T> && other);

    const T* Start() const;
    size_t Size() const;
};

#include "SomeDataImpl.h"
#pragma once

template<typename T>
SomeData<T>::SomeData(std::vector<T> && otherData) : thisData(std::move(otherData)) { }

template<typename T>
const T* SomeData<T>::Start() const {
    return thisData.data();
}

template<typename T>
size_t SomeData<T>::Size() const {
    return sizeof(T) * thisData.size();
}
#pragma一次
#包括
模板
类数据
{
std::矢量数据;
公众:
一些数据(标准::向量和其他);
常量T*Start()常量;
大小\u t大小()常量;
};
#包括“SomeDataImpl.h”
SomeDataImpl.h

#pragma once
#include <vector>

template<typename T>
class SomeData
{
    std::vector<T> thisData;

public:
    SomeData(std::vector<T> && other);

    const T* Start() const;
    size_t Size() const;
};

#include "SomeDataImpl.h"
#pragma once

template<typename T>
SomeData<T>::SomeData(std::vector<T> && otherData) : thisData(std::move(otherData)) { }

template<typename T>
const T* SomeData<T>::Start() const {
    return thisData.data();
}

template<typename T>
size_t SomeData<T>::Size() const {
    return sizeof(T) * thisData.size();
}
#pragma一次
模板
SomeData::SomeData(std::vector&&otherData):thisData(std::move(otherData)){}
模板
常量T*SomeData::Start()常量{
返回thisData.data();
}
模板
size\u t SomeData::size()常量{
返回sizeof(T)*thisData.size();
}
用法示例:

#include <iostream>
#include "SomeData.h"

template<typename T>
void Print(const T * start, size_t size) {
    size_t toPrint = size / sizeof(T);
    size_t printed = 0;

    while(printed < toPrint) {
        std::cout << *(start + printed) << ", " << start + printed << std::endl;
        ++printed;
    }
}

int main () {
    std::vector<int> ints;
    ints.push_back(1);
    ints.push_back(2);
    ints.push_back(3);

    SomeData<int> someData(std::move(ints));
    Print<int>(someData.Start(), someData.Size());

  return 0;
}
#包括
#包括“SomeData.h”
模板
无效打印(常数*开始,大小){
size\u t toPrint=尺寸/尺寸(t);
打印尺寸=0;
while(打印STD::CUT< P>你不能用任何一种可移植的方式来做这件事,但是你可以用一种可能在大多数C++实现中工作的方式来做。这个代码在VS 2017快速测试之后似乎可以工作。
#include <iostream>

#include <vector>

using namespace std;

template <typename T>
T* HACK_stealVectorMemory(vector<T>&& toStealFrom)
{
    // Get a pointer to the vector's memory allocation
    T* vectorMemory = &toStealFrom[0];

    // Construct an empty vector in some stack memory using placement new
    unsigned char buffer[sizeof(vector<T>)];
    vector<T>* fakeVector = new (&buffer) vector<T>();

    // Move the memory pointer from toCopy into our fakeVector, which will never be destroyed.
    (*fakeVector) = std::move(toStealFrom);

    return vectorMemory;
}

int main()
{
    vector<int> someInts = { 1, 2, 3, 4 };
    cout << someInts.size() << endl;

    int* intsPtr = HACK_stealVectorMemory(std::move(someInts));

    cout << someInts.size() << endl;

    cout << intsPtr[0] << ", " << intsPtr[3] << endl;

    delete intsPtr;
}

你需要接管整个向量。如果
vector
detach
功能就好了……但它没有t@T.C.:但是我没有地方存储它--输入生成和释放发生在程序的两个独立部分中。我不理解对齐存储的需要。为什么不只
unique_ptr temp(新向量(移动(输入)))
?另外,只有当
T
是一种可销毁类型时,您的解决方案才有效,否则您需要对每个元素调用
allocator::destroy
。要回答您的问题,从
向量
中接管内存不是一个简单的方法,您可能可以使用自定义分配器来完成一些事情,但我只是点击当前解决方案。叹气-库使用错误回调签名的另一种情况。如果回调签名是
void(*回调)(T*data,size\u T size,void*user\u data)
开始处理(T*data,size\u T size,void*userdata)
您将有一个简单的解决方案。看起来不错。如果我无法找到避免全局变量的方法,我将在这里撒上一点
std::mutex
std::unique\u ptr
,应该没问题。谢谢!