shared_ptr 到可变长度结构
Posted
技术标签:
【中文标题】shared_ptr 到可变长度结构【英文标题】:shared_ptr to variable length struct 【发布时间】:2014-03-21 01:04:34 【问题描述】:如何组合变长结构成语
struct Data
std::size_t size;
char data[];
;
使用 make_shared 习惯用法,它本质上做同样的事情,这样我就可以在一个包含引用计数结构头和结构数据的连续内存块上获得一个 shared_ptr。
即像
// allocate an extra 30 bytes for the data storage
shared_ptr<Data> ptr = allocate_shared<Data>( vls_allocator(30) );
【问题讨论】:
您正在使用 C++。只需为Data
定义一个构造函数,该构造函数接受为数组分配的大小,然后执行shared_ptr<Data> ptr = shared_ptr<Data>(new Data(30))
。 C++ 中的struct
仅表示class
,默认为public
,而不是成员的private
。
注意灵活数组是一个扩展,我猜你使用的是 gcc 或 clang。
添加构造函数在这里没有任何作用。当构造函数被调用时,内存已经分配好了,做任何事情都为时已晚。
是的,灵活数组(感谢您提醒我的名字)是一个扩展,一个广泛可用且非常有用的扩展。
你不会简单地在实例化列表中进行分配吗? IE。 Data(size_t size):size(size), data(vls_allocator(size))
?
【参考方案1】:
您可以使用 boosts 侵入式共享指针来实现这一点,(不确定 C++11 是否直接支持)。
http://www.boost.org/doc/libs/1_55_0/libs/smart_ptr/intrusive_ptr.html
您需要创建一个更大的结构,其中包含引用计数,并且您的指针将指向 laregr 结构,而不是它包含的 Data
成员。
您还需要将您的释放函数连接到这些指针。
注意:我怀疑有办法让共享指针指向 Data
成员,而不是包装器 - 但上次我使用 boost shared_ptr
代码这样做需要一些有趣的技巧。
【讨论】:
这确实是个不错的选择,shared_ptr 的线程安全引用计数器在这种情况下可能不是必需的,我将不得不考虑更多。 我认为您仍然可以使用线程安全计数器 - 但您需要查看文档。真正的损失是失去了 weak_ptr 支持。 (我不认为有一个侵入性的变体支持它..事实上我不认为它可以用你想要的内存布局来完成。( 我确定我确实需要线程安全原子计数器,但将 intrusive_ptr 与 in the boost atomic documentation 描述的原子用法相结合是一种解决方案【参考方案2】:在我看来,这是一个重要的要求,尤其是在编写必须与低级系统功能交互或提供接口的代码时(在现代 C++ 世界之外,但仍符合现代代码)。
解决方案有两个部分。首先将缓冲区分配为智能指针,然后以安全的方式将其转换为正确的类型,从而通过代码分析/指南检查器。
第二部分通常与“std::reinterpret_pointer_cast
在 C++20 之前缺少链接的第一部分是创建一个安全的 shared_ptr 到可变长度的缓冲区,例如由 "std::make_shared
I stumbled upon this macro 当与 std::reinterpret_pointer_cast 结合使用时,它会为您提供一个很好的“polyfill”,以在干净的代码中实现相同的结果,直到 C++20 普遍可用。
// C++20 polyfill for missing "make_shared<T[]>(size_t size)" overload.
template<typename T>
inline std::shared_ptr<T> make_shared_array(size_t bufferSize)
return std::shared_ptr<T>(new T[bufferSize], [](T* memory) delete[] memory; );
// Creates a smart pointer to a type backed by a variable length buffer, e.g. system structures.
template<typename T>
inline std::shared_ptr<T> CreateSharedBuffer(size_t byteSize)
return std::reinterpret_pointer_cast<T>(make_shared_array<uint8_t>(byteSize));
例子:
size_t privilegesSize = sizeof(TOKEN_PRIVILEGES) + (sizeof(LUID_AND_ATTRIBUTES) * privilegesCount);
auto tokenPrivileges = CreateSharedBuffer<TOKEN_PRIVILEGES>(privilegesSize);
唯一的副作用显然是在分配期间会发生第二次内存访问,但鉴于 C++20 即将到来,与担心额外的处理器相比,现在使用干净健壮的代码运行您的解决方案可能更高效、更健壮在短时间内循环一两个周期。
当 C++20 可用时,您所要做的就是删除“polyfill”模板并将对它的调用从“make_shared_array”重命名或替换为“make_shared”(使用新的 C++20 重载)。如果下面的 C++20 解决方案可以接受,可以选择放弃整个 CreateSharedBuffer 宏:
auto data = std::reinterpret_cast<Data>(std::make_shared<uint8_t>(sizeof(Data) + (sizeof(char) * 30)));
【讨论】:
以上是关于shared_ptr 到可变长度结构的主要内容,如果未能解决你的问题,请参考以下文章
SAL 注释:std::shared_ptr 的 _Ret_maybenull_