How to handle heap allocated fixed length byte buffers?

问题描述 投票:0回答:2

在 C 语言中,我们使用

char*
指向由
malloc
分配的内存块,并在单独的大小/长度变量中跟踪大小。

C++ 等价物是什么?据我所知,到目前为止,大多数人都使用

std::vector
。通常
.resize()
被调用以分配所需的内存,然后数据可以被
memcpy
ed 到
.data()
.

我个人认为

std::vector
不应该用作固定大小的字节缓冲区。

假设我们有一个库,它返回一个代表固定大小字节缓冲区的

std::vector<byte>
。我们可能会遇到这些问题:

  • 不必要的动态: 当我们将固定大小的字节缓冲区传递给各种函数时,它们可以自由
    push_back
    insert
    新数据到缓冲区。目的是固定大小的缓冲区。动态调整大小的能力是不必要的、不需要的和令人困惑的。
  • 笨拙:如果我们想避免一直复制缓冲区,我们必须使用
    std::move
    的组合,通过引用传递并传递
    .data()
    .size()
    。而且,如果您在某处忘记了
    std::move
    ,整个内容将被意外复制。在我看来,这不必要地过于复杂,而且很烦人。在许多情况下,我认为分配有(
    malloc
    /
    free
    new[]
    /
    delete[]
    并包裹在
    std::span
    ?)中的原始数组将更易于管理且不易混淆,尽管肯定有更好的 C++解决方案?
  • 默认初始化:人们在默认初始化所有元素时使用
    std::vector
    存储原始缓冲区!?
  • 与 C 库不兼容: 无法从向量析构函数的掌握中释放底层缓冲区。因此,如果您将
    .data()
    传递给例如
    zip_add_file(char* name, char* buffer, size_t len)
    函数,则该向量必须保持活动状态,直到调用
    zip_write_files()
    以释放所有给定的缓冲区。即使它死了,它也会尝试释放已经释放的内存。并且无法指定分配器或释放器,因此 zip 库无论如何都会在释放缓冲区时导致未定义的行为。

那么有没有更好的方法呢?

c++ arrays vector smart-pointers unique-ptr
2个回答
0
投票

unique_ptr<byte[]>
几乎正是我们所需要的。我们可以轻松直观地用
make_unique_for_overwrite<byte[]>(N)
制作一个。它不默认初始化元素。如果我们从我们的库函数而不是
std::vector<byte>
返回它,它更容易传递和管理更灵活(例如,我们可以轻松转换为
shared_ptr
),它是一个固定大小的数组,不会出现具有调整大小、push_back 或插入的功能,这非常有意义。可以使用
[]
运算符按索引访问字节。如果我们想要与前面提到的 C 库兼容,我们可以通过传入分配有
unique_ptr<byte[]>
malloc
删除器的缓冲区来创建
free
。如果我们希望 C 库接管,则只需调用
.release()
。无痛

缺点:

不幸的是,它只是一个指向基本

byte[]
数组的指针,所以没有可用的
.size()
unique_ptr
确实通过使用不同的默认删除器并且只为数组定义
[]
运算符来区别对待数组和非数组,所以我觉得可以添加
.size()
?


0
投票

除了不是线程安全的,我认为这是一个很好的 hack。

运行时大小的数组:

template<typename T>
class dynarray {
private:
    static unordered_map<const dynarray*, size_t> sizes;
    explicit dynarray(size_t n) {
        sizes[this] = n;
    }
    ~dynarray() {
        sizes.erase(this);
        free(this);
    }
public:
    static dynarray* create(size_t n) {
        return new (malloc(n * sizeof(T))) dynarray(n);
    }
    static void deleter(void* ptr) {
        ((dynarray*) ptr)->~dynarray();
    }
    size_t size() const {
        return sizes.at(this);
    }
    T& operator[](int index) {
        return data[index];
    }
    T data[0];
};

template<typename T>
unordered_map<const dynarray<T>*, size_t> dynarray<T>::sizes;

创作者功能:

template<typename T>
auto make_unique_array(size_t n) {
    return unique_ptr<dynarray<T>, decltype(&free)>(dynarray<T>::create(n), dynarray<T>::deleter);
}

例子:

string abc = "abc";
auto buffer = make_unique_array<char>(abc.size());
memcpy(buffer.get(), abc.c_str(), abc.size());

(*buffer)[0]; // 'a'
(*buffer)[1]; // 'b'
(*buffer)[2]; // 'c'
buffer->size(); // 3

警告:我没有正确测试它。

© www.soinside.com 2019 - 2024. All rights reserved.