删除C++17 pmr内存资源分配的多态对象的正确方案
解决方案
当前静态存储内存资源指针的方案存在线程不安全、需手动维护状态易出错的问题,可采用以下两种标准实现解决:
方案1:基类新增虚销毁接口 + 内置存储分配器指针
核心逻辑
- 给
Node基类新增非静态成员,存储当前对象所用的std::pmr::memory_resource指针 - 新增纯虚
destroy()方法,各派生类自行实现销毁逻辑:先调用自身析构,再用对应分配器释放内存 - 完全移除静态
s_deleterResource变量相关逻辑
核心代码修改
struct Node { using allocator_type = std::pmr::polymorphic_allocator<Node>; std::pmr::string name; // 新增:存储当前对象使用的内存资源 std::pmr::memory_resource* m_res; template <class TNode > static TNode *create(std::string_view name, std::pmr::memory_resource *res) { std::pmr::polymorphic_allocator<TNode> alloc(res); auto ptr = alloc.allocate(1); ::new (ptr) TNode(alloc, res); ptr->name = name; return ptr; } virtual ~Node() { std::cerr << "Destructing node: " << name << std::endl; } // 新增:纯虚销毁接口 virtual void destroy() noexcept = 0; protected: // 构造函数新增res参数初始化成员 Node(const allocator_type& alloc, std::pmr::memory_resource* res) : name(alloc), m_res(res) {} }; struct CompoundNode : Node { friend struct Node; using allocator_type = std::pmr::polymorphic_allocator<CompoundNode>; void addChild(Node *child) { m_children.push_back(child); } ~CompoundNode() override { for(auto child : m_children) { // 替换原来的delete,调用destroy child->destroy(); } } // 实现destroy方法 void destroy() noexcept override { std::pmr::polymorphic_allocator<CompoundNode> alloc(m_res); this->~CompoundNode(); alloc.deallocate(this, 1); } protected: explicit CompoundNode(const allocator_type& alloc, std::pmr::memory_resource* res) : Node(alloc, res), m_children(alloc) { } std::pmr::vector< Node * > m_children; }; struct LeafNode : Node { friend struct Node; using allocator_type = std::pmr::polymorphic_allocator<LeafNode>; ~LeafNode() override = default; // 实现destroy方法 void destroy() noexcept override { std::pmr::polymorphic_allocator<LeafNode> alloc(m_res); this->~LeafNode(); alloc.deallocate(this, 1); } protected: explicit LeafNode(const allocator_type& alloc, std::pmr::memory_resource* res) : Node(alloc, res), m_payload(77, alloc) { } std::pmr::vector< uint8_t > m_payload; };
main函数修改
int main() try { std::array< uint8_t, 1000 > buf; std::pmr::monotonic_buffer_resource bufferRes(buf.data(), buf.size(), std::pmr::null_memory_resource()); VerboseMemResource res("buffered resource", &bufferRes); auto root = Node::create<CompoundNode>("root", &res); root->addChild(Node::create<LeafNode>("child1", &res)); root->addChild(Node::create<LeafNode>("child2", &res)); std::cerr << "Beginning tree deletion..\n"; // 直接调用destroy,不需要操作静态变量 root->destroy(); return 0; } catch(std::exception& ex) { std::cerr << "Exception: " << ex.what() << std::endl; return 1; }
方案2:返回带自定义删除器的智能指针
不需要修改Node类的虚函数,直接在create方法中返回绑定了删除逻辑的std::unique_ptr,删除器中自带对应内存资源指针,完全遵循RAII规范,无需手动调用销毁方法:
核心代码修改
struct Node { using allocator_type = std::pmr::polymorphic_allocator<Node>; std::pmr::string name; // 修改create返回值为带自定义删除器的unique_ptr template <class TNode > static auto create(std::string_view name, std::pmr::memory_resource *res) { std::pmr::polymorphic_allocator<TNode> alloc(res); auto ptr = alloc.allocate(1); ::new (ptr) TNode(alloc); ptr->name = name; // 绑定删除逻辑,自带当前res指针 auto deleter = [res](TNode* p) noexcept { std::pmr::polymorphic_allocator<TNode> alloc(res); std::destroy_at(p); alloc.deallocate(p, 1); }; return std::unique_ptr<TNode, decltype(deleter)>(ptr, deleter); } virtual ~Node() { std::cerr << "Destructing node: " << name << std::endl; } protected: Node(const allocator_type& alloc) : name(alloc) {} }; struct CompoundNode : Node { friend struct Node; using allocator_type = std::pmr::polymorphic_allocator<CompoundNode>; // 存储带删除器的unique_ptr void addChild(std::unique_ptr<Node>&& child) { m_children.push_back(std::move(child)); } ~CompoundNode() override = default; // 不需要手动遍历释放,智能指针自动处理 protected: explicit CompoundNode(const allocator_type& alloc) : Node(alloc), m_children(alloc) { } std::pmr::vector<std::unique_ptr<Node>> m_children; };
main函数修改
int main() try { std::array< uint8_t, 1000 > buf; std::pmr::monotonic_buffer_resource bufferRes(buf.data(), buf.size(), std::pmr::null_memory_resource()); VerboseMemResource res("buffered resource", &bufferRes); auto root = Node::create<CompoundNode>("root", &res); root->addChild(Node::create<LeafNode>("child1", &res)); root->addChild(Node::create<LeafNode>("child2", &res)); std::cerr << "Beginning tree deletion..\n"; // 不需要手动调用销毁,root离开作用域自动释放所有节点 return 0; } catch(std::exception& ex) { std::cerr << "Exception: " << ex.what() << std::endl; return 1; }
选型建议
- 对性能敏感、节点数量极大的场景选方案1,额外开销仅为每个对象多一个指针的内存占用
- 优先考虑代码安全性、避免手动管理销毁的场景选方案2,完全符合现代C++的RAII规范,不存在漏释放的风险
内容的提问来源于stack exchange,提问作者pem
相关产品推荐
相关产品推荐

