通过make_shared封装动态数组shared_ptr
Wrap dynamic array with shared_ptr by make_shared
我想写入一些字节数组。为了使用现代c++,我决定使用智能指针。
#include <memory>
#include <cstdint>
using namespace std;
void writeUint32_t(uint32_t value, unsigned char* p){
*p = static_cast<unsigned char>((value >> 24) & 0xFF);
*(++p) = static_cast<unsigned char>((value >> 16) & 0xFF);
*(++p) = static_cast<unsigned char>((value >> 8) & 0xFF);
*(++p) = static_cast<unsigned char>((value ) & 0xFF);
}
int main(){
auto buf = make_shared<unsigned char[]>(512);
uint32_t data = 1234;
writeUint32_t(data, buf.get() + 8);
}
但是,我收到以下编译错误:
u.cpp:15:37: error: invalid use of array with unspecified bounds
writeUint32_t(data, buf.get() + 8);
^
u.cpp:15:38: error: cannot convert ‘unsigned char (*)[]’ to ‘unsigned char*’ for argument ‘2’ to ‘void writeUint32_t(uint32_t, unsigned char*)’
writeUint32_t(data, buf.get() + 8);
我使用g++ (Ubuntu 5.4.0-6ubuntu1~16.04.2) 5.4.0 20160609
在这种情况下,有办法使用智能指针吗?
不要使用原始数组std::make_shared
,它不会像你期望的那样构造一个数组,而是会尝试创建一个指向你指定的类型的指针,即unsigned char[]
。这就是为什么当使用get()
时得到unsigned char (*)[]
,如错误消息所示。
并且std::shared_ptr
默认会删除delete
的指针,而不是delete[]
,后者应该用于数组。您需要为它指定一个自定义的删除器,但是std::make_shared
不允许您指定它。
您可以(1)直接初始化std::shared_ptr
并指定删除器,如
std::shared_ptr<unsigned char> buf(new unsigned char[512], [](unsigned char* p)
{
delete[] p;
});
(2)使用std::unique_ptr
代替,它为数组提供了一个指定的版本,包括在解分配时调用delete[]
和提供operator[]
(std::shared_ptr
将从c++ 17开始支持它)。
auto buf = std::make_unique<unsigned char[]>(512);
(3)考虑std::vector<unsigned char>
和std::array<unsigned char>
我建议您使用std::vector<unsigned char> vec(512);
,包装连续的动态数组正是它的目的。获取原始缓冲区指针非常简单,如vec.data()
;
如果需要共享vector,那么仍然可以使用智能指针
auto p_vec = make_shared<vector<unsigned char>>(512);
您将获得引用计数的好处,几乎没有开销,因为使用vector, 和,您将获得整个vector API。
如果你真的想使用shared_ptr
和数组(而不是像StoryTeller建议的向量),你的类型应该是unsigned char
而不是unsigned char[]
。为了确保数组被正确删除,您需要指定一个数组删除器来传递给shared_ptr
构造函数(因此您不能使用make_shared
,因为它不允许您指定删除器):
auto buf = std::shared_ptr<unsigned char>(new unsigned char[512], std::default_delete<unsigned char[]>());
不能使用:
std::make_shared<unsigned char[]>(512);
那是因为Type[]
没有模板专门化。那是因为实际上std::shared_ptr<T>
还不支持operator[]
。它将在新标准 c++ 17:
操作符[](C + + 17)
一个解决方案可以使用STL容器,或者IMO更好:
std::unique_ptr
.
事实上,std::unique_ptr
支持operator[]
。
从c++ 14开始,可以:
auto buffer = std::make_unique<unsigned char[]>(512);
buffer[index];
它是异常安全的,几乎为0开销,并且它可以用作数组缓冲区。
此外,通过正确调用delete[]
操作符可以正确地处理缓冲区的释放。
如果您确实需要shared_ptr<T[]>
而不是unique_ptr<T[]>
,我建议使用boost::shared_ptr
。支持T[]
.
优点:
- c++ 20仍然不支持
shared_ptr<T[]>
。尝试了g++ 11.3和VS2022. - 使用
std::shared_ptr<T>(new T[size], deleter)
表达式创建数组效率不高,因为引用计数器的内存被分配到其他地方。 -
std::shared_ptr<std::vector>
也不是有效的,因为shared_ptr指向指针。为了访问数据,你需要再解引用一次。
boost::shared_ptr
解决了所有的问题:
- 数据和参考计数器的内存是一次性分配的,并且在附近分配,从而提高了数据的局域性
shared_ptr
指向数据本身,而不是像std::vector
那样指向另一个指针。#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
int main()
{
auto arr = boost::make_shared<unsigned char[]>(512);
}
- Mongodb c++驱动程序:如何查询元素的数组
- 将数组的地址分配给变量并删除
- 从C++本机插件更新Vector3数组
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 数组索引的值没有增加
- 将对象数组的引用传递给函数
- 为char数组调整zlib-zpipe
- 2D数组来自文本输入,中间有空格
- std::向量与传递值的动态数组
- 在c++中用vector填充一个简单的动态数组
- 使用strcpy将char数组的元素复制到另一个数组
- 使用指针从C++中的数组中获取最大值
- C++使用整数的压缩数组初始化对象
- 告诉一个 const char 数组,除了编译时 C 样式的字符串外,它不以 '