从共享库安全传递指针:std::default_delete特化是否必要?
共享库返回std::unique_ptr时,是否需要特化std::default_delete?
先给结论:要不要特化,取决于你的SomeClass析构函数的可见性,以及共享库和客户端的内存分配器是否一致。
为什么会有人加这个特化?
你的场景是共享库用new创建对象,客户端用unique_ptr接管。这里藏着两个潜在的坑:
- 析构函数找不到:如果
SomeClass是接口(抽象类),析构函数的实现只在共享库里,客户端头文件只有声明(比如纯虚析构)。这时候客户端的default_delete执行delete时,需要调用析构函数,但客户端没有这个函数的定义,要么链接报错,要么运行时出现未定义行为。 - 分配器不匹配:如果共享库和客户端用了不同版本的标准库,或者编译选项差异导致堆分配规则不同,客户端用自己的
delete去释放共享库new的对象,必然会出问题——相当于用A的规则分配内存,却用B的规则释放,大概率触发崩溃。
这个特化到底解决了什么问题?
它把delete的操作逻辑挪到共享库里执行:
- 特化的
std::default_delete<SomeClass>是在共享库中编译的,它的operator()里的delete会使用共享库的内存分配器,和创建对象时的new完全匹配,避免分配释放不兼容。 - 同时,析构函数的调用也在共享库中完成,客户端不需要看到析构函数的实现,自然不会有链接问题。
什么时候可以不用特化?
满足以下所有条件,就没必要多此一举:
SomeClass的析构函数在客户端可见(比如头文件里直接写内联定义,或者客户端能链接到析构的实现代码)。- 共享库和客户端用的是完全一样的C++标准库,编译选项(尤其是堆相关的)也完全一致,确保分配器相同。
- 你能确保
delete和new的代码在同一个编译单元(也就是都在共享库)。
最后总结
如果SomeClass是只在共享库中实现析构的接口,或者没法保证客户端和共享库的分配器一致,这个特化必须加——不然大概率踩生命周期管理的坑。要是上述风险都不存在,用默认的default_delete就够了。
内容的提问来源于stack exchange,提问作者Piotr G
相关产品推荐
相关产品推荐

