从std::vector接管内存

23

我使用一个操作大量数据的外部库。该数据通过原始指针和长度传入。该库不声明指针的所有权,但在完成数据操作时调用提供的回调函数(带有相同的两个参数)。

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

我的当前解决方案如下:

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);

并且,在回调函数中:

void callback(T * data, size_t size) {
    std::allocator<T>().deallocate(data, size);
}

这个解决方案可行,因为标准分配器的deallocate函数忽略了它的第二个参数(元素计数),并简单地调用::operator delete(data)。如果没有这样做,可能会发生糟糕的事情,因为输入向量的size可能比其capacity小得多。
我的问题是:是否有一种可靠(就C++标准而言)的方法来接管std::vector的缓冲区,并在以后的某个时间手动释放它?

1
你需要接管整个向量。 - T.C.
1
希望vector有一个detach函数会很好...但它没有。 - M.M
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Grzegorz Herman
1
我不理解需要对齐存储的原因。为什么不只是使用 unique_ptr<vector<T>> temp(new vector<T>(move(input)));?此外,您的解决方案仅适用于 T 是可平凡析构类型,否则您需要在每个元素上调用 allocator<T>::destroy。回答您的问题,没有简单的方法可以接管来自 vector 的内存,您可能可以使用自定义分配器完成某些操作,但我建议仍然坚持当前的解决方案。 - Praetorian
3
叹息 - 又是一个库使用错误的回调函数签名。如果回调函数签名为 void (*callback)(T * data, size_t size, void * user_data) 并且 startProcessing(T* data, size_t size, void * userdata),你就有了通向解决方案的简单途径。 - Michael Anderson
显示剩余19条评论
3个回答

5

您无法从向量中拥有内存,但可以通过另一种方式解决根本问题。

这是我处理的方法-由于静态全局变量和不具有线程安全性,因此它有点hacky,但可以通过在访问registry对象时添加简单的锁定来实现。

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::vector<T> * input = ...
my_startProcessing(input);

但是要注意!如果在调用my_startProcessing之后添加或删除输入元素,可能会导致库缓冲区失效。您可能可以更改向量中的值,因为我认为这将正确地写入数据,但这也取决于库允许什么。此外,如果T=bool,则此方法不起作用,因为std::vector::data()无法正常工作。

看起来不错。如果我找不到避免全局变量的方法,我将加入一些std::mutexstd::unique_ptr,那就没问题了。谢谢! - Grzegorz Herman

0

你可以创建一个基于向量的自定义类。

关键点在于在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"

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();
}

使用示例:

#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;
}

-1

你无法以任何可移植的方式完成这个任务,但是你可以使用一种在大多数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;
}

输出:

4
0
1, 4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接