是否误解shared_ptr默认参数作用域?析构器未触发问题咨询
先看看你的代码:
#include <iostream> #include <memory> using Foo = int; using FooPtr = std::shared_ptr<Foo>; FooPtr makeFoo() { FooPtr f{ new Foo(), [](Foo* ptr) { delete ptr; std::cerr << "!\n"; } }; return f; } void bar(FooPtr p = {}) { p = makeFoo(); } int main() { bar(); }
预期输出:
!
实际情况: 无输出(析构器未被调用)
你本来以为bar()执行完返回时,shared_ptr会销毁并触发自定义删除器输出!,但在CentOS7的GCC4.8.5上没看到预期结果,对吧?
问题根源:GCC 4.8.x的shared_ptr默认参数bug
这是GCC 4.8系列版本的一个已知缺陷:当你把shared_ptr作为函数的默认参数(比如这里的FooPtr p = {}),然后在函数内部给这个参数赋值时,编译器生成的代码会出问题——赋值后的shared_ptr在函数退出时没有正确触发析构逻辑,包括你写的自定义删除器。
这个bug在GCC 5及以后的版本里已经被修复了,如果你能升级编译器,问题应该直接就解决了。
针对GCC4.8.x的临时解决办法
如果暂时没法升级编译器,给你几个可行的修改方案:
方案1:用函数重载替代默认参数
把带参数和不带参数的情况分开写,绕开默认参数的坑:
void bar(FooPtr p) { p = makeFoo(); } void bar() { FooPtr p; p = makeFoo(); }
方案2:在函数内显式重置指针
强制让shared_ptr在函数结束前触发析构:
void bar(FooPtr p = {}) { p = makeFoo(); // 显式重置,确保删除器被调用 p.reset(); }
方案3:直接用局部变量(最推荐)
其实你这个场景根本不需要用默认参数,直接在函数里声明局部的shared_ptr就行,写法更清晰,还能避开bug:
void bar() { FooPtr p = makeFoo(); }
这样修改后,再运行代码应该就能看到预期的!输出了。
内容的提问来源于stack exchange,提问作者Lightness Races in Orbit
相关产品推荐
相关产品推荐

