如何无内存泄漏地深拷贝含Boost侵入式列表的Map?
解决Boost侵入式列表Map深拷贝的内存泄漏问题
核心问题分析
Boost侵入式容器(如list)的设计原则是不管理元素内存,容器仅维护元素间的链接关系,因此深拷贝时手动创建的Str对象需要我们自行负责销毁,否则会导致内存泄漏。你尝试通过继承list添加析构清理逻辑的思路是可行的,编译错误源于unordered_map::insert的初始化列表推导问题,可通过调整插入方式解决。
方案1:继承侵入式列表实现自动清理(推荐,若读取线程可兼容新类型)
此方案让拷贝后的列表在销毁时自动清理元素,无需手动调用清理函数:
- 定义带自动清理逻辑的列表子类:
#include <boost/intrusive/list.hpp> #include <unordered_map> using namespace boost::intrusive; struct Tag; using Hook = list_base_hook< tag<Tag> >; class Str : public Hook { public: Str(int n) : n_(n) {} Str(const Str& s) : n_(s.n_) {} int n_; }; // 原列表类型,供写入线程使用 using List = list< Str, cache_last<true>, base_hook<Hook> >; using Map = std::unordered_map<std::string, List>; // 带自动清理的列表子类,供拷贝后的读取线程使用 class List_ : public List { public: // 显式默认移动构造/赋值,确保容器移动操作正常 List_(List_&&) = default; List_& operator=(List_&&) = default; ~List_() { while (!empty()) { Str* node = &front(); pop_front(); delete node; } } }; // 拷贝专用的Map类型 using CopyMap = std::unordered_map<std::string, List_>;
- 修改深拷贝函数,使用
emplace避免初始化列表编译错误:
void deep_copy_map(const Map& orig, CopyMap& copy) { for (const auto& entry : orig) { List_ list_copy; for (const auto& str : entry.second) { // 拷贝构造新的Str对象并加入列表 list_copy.push_back(*new Str(str)); } // 使用emplace直接构造键值对,绕过初始化列表的推导问题 copy.emplace(entry.first, std::move(list_copy)); } }
当CopyMap对象被销毁时,每个List_的析构函数会自动遍历销毁所有Str元素,彻底避免内存泄漏。
方案2:不修改容器类型,手动清理拷贝元素(兼容原读取线程逻辑)
若读取线程无法修改类型,必须使用原Map类型,可在读取线程使用完拷贝的Map后手动清理元素:
- 深拷贝函数保持原逻辑,调整插入方式避免编译问题:
void deep_copy_map(const Map& orig, Map& copy) { for (const auto& entry : orig) { List list_copy; for (const auto& str : entry.second) { list_copy.push_back(*new Str(str)); } // 使用std::make_pair构造可移动的键值对 copy.insert(std::make_pair(entry.first, std::move(list_copy))); } }
- 提供清理函数,读取线程使用完拷贝的Map后调用:
void cleanup_map(Map& map) { for (auto& entry : map) { auto& list = entry.second; while (!list.empty()) { Str* node = &list.front(); list.pop_front(); delete node; } } }
如果读取线程无法直接调用清理函数,可封装成RAII类自动触发清理:
class MapWrapper { public: Map map; ~MapWrapper() { cleanup_map(map); } };
深拷贝时直接构造MapWrapper对象,读取线程使用wrapper.map即可,MapWrapper销毁时会自动清理所有Str元素。
内容的提问来源于stack exchange,提问作者CforLinux
相关产品推荐
相关产品推荐

