在 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
存储原始缓冲区!?.data()
传递给例如 zip_add_file(char* name, char* buffer, size_t len)
函数,则该向量必须保持活动状态,直到调用 zip_write_files()
以释放所有给定的缓冲区。即使它死了,它也会尝试释放已经释放的内存。并且无法指定分配器或释放器,因此 zip 库无论如何都会在释放缓冲区时导致未定义的行为。那么有没有更好的方法呢?
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()
?
除了不是线程安全的,我认为这是一个很好的 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
警告:我没有正确测试它。