将shared_ptr存入std::map后对象提前析构、引用计数为0如何解决?
问题根因
你遇到的所有现象核心是两个点:
std::map的operator[]在key不存在时会自动隐式插入一个默认构造的对应值std::map::insert操作在key已经存在的情况下会直接插入失败,不会覆盖原有值
执行流程拆解
你的代码实际执行顺序如下:
- 执行
auto temp = datas["key"]:此时map中不存在key,operator[]自动往map插入一个空的std::shared_ptr<Test>,然后将这个空智能指针拷贝给temp,此时temp是空值,引用计数为0 - 判定
temp为空,调用std::make_shared<Test>创建对象,赋值给temp,此时temp的引用计数为1 - 执行
datas.insert(std::make_pair("key", temp)):因为key已经被第一步的operator[]插入到map中,insert直接失败,你创建的智能指针并没有被存入map - if代码块结束,局部变量
temp被销毁,它持有的智能指针引用计数从1降到0,对应的Test对象被析构 - 后面执行
auto other = datas["key"]:拿到的是第一步自动插入的空智能指针,空智能指针的use_count默认就是0,所以输出0
你补充测试中注释掉if块的现象也符合这个逻辑:直接调用datas["key"]只会自动插入空智能指针,没有创建Test对象自然不会调用析构,空指针的use_count自然是0。
正确的实现方式
这里给两种常用的写法,都可以避免你遇到的问题:
写法1:直接修改operator[]返回的引用
避免拷贝operator[]的返回值,直接用引用接收,修改引用就等价于修改map中存储的值:
if (true) { // 用引用接收,不要做拷贝 auto& temp = datas["key"]; if (!temp) { std::cout << "create new one" << std::endl; // 直接给map中的元素赋值 temp = std::make_shared<Test>(); } else { std::cout << "already exists,pass" << std::endl; } std::cout << "temp use count:" << temp.use_count() << std::endl; }
写法2:用find先判断,避免隐式插入
如果不希望key不存在时自动插入空值,可以先用find判断是否存在,不存在再插入:
if (true) { auto it = datas.find("key"); if (it == datas.end()) { std::cout << "create new one" << std::endl; auto temp = std::make_shared<Test>(); datas.emplace("key", temp); std::cout << "temp use count:" << temp.use_count() << std::endl; } else { std::cout << "already exists,pass" << std::endl; std::cout << "temp use count:" << it->second.use_count() << std::endl; } }
注意事项
- 仅当你确定需要在key不存在时插入默认值的时候,才使用
operator[],单纯查询优先用find std::map的插入接口区别要注意:insert仅key不存在时插入,insert_or_assign会覆盖已有key的值,C++17新增的try_emplace在key不存在时才构造元素,性能比insert更高
内容的提问来源于stack exchange,提问作者宝蓝娃娃
相关产品推荐
相关产品推荐

