从std :: vector接管内存

我使用一个外部库,它可以处理大量的数据。 数据通过原始指针传递,并加上长度。 该库不声明指针的所有权,但在完成数据时调用提供的回调函数(具有相同的两个参数)。

通过使用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的缓冲区并在稍后时间“手动”释放它?


你不能从矢量中获得内存的所有权,但你可以通过另一种方式解决你的潜在问题。

下面是我如何处理它 - 由于静态全局变量而不是线程安全,它有点冒险,但可以通过一些简单的锁定来访问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<bool>::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"
    

    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;
    }
    
    链接地址: http://www.djcxy.com/p/23553.html

    上一篇: taking over memory from std::vector

    下一篇: Defining projection to map to nested case classes