C++中unique_ptr自定义删除器重写失效问题及解决咨询
问题:自定义unique_ptr删除器失效导致free()错误
我在C++中编写了如下代码,定义了继承自deleter的null_deleter,试图重写operator(),让自定义的unique_ptr(using unique_ptr = std::unique_ptr<T, deleter<T>>)不执行释放操作。但运行时仍调用deleter<T>的逻辑,出现free(): invalid pointer错误。请问为何会这样?如何实现修改unique_ptr删除器的行为?
#include <iostream> #include <memory> class MyType{ public: ~MyType() { std::cout<<"Destructor called"; } }; template<typename T> struct deleter { deleter(void) {} template<typename U> deleter(deleter<U> const&, std::enable_if_t<std::is_convertible<U*, T*>::value>* = nullptr) {} virtual void operator()(T* p) const { std::cout<<"deleter called"; static_assert(sizeof(T) > 0, ""); if (p) { p->~T(); free(const_cast<std::remove_const_t<T>*>(p)); } } }; template<typename T> struct null_deleter : public deleter<T> { void operator()(T* ptr) const override { // Do nothing std::cout<<"null deleter called"; } }; template<typename T> using unique_ptr = std::unique_ptr<T, deleter<T>>; int main() { MyType myType; std::cout<<"hello"; unique_ptr<MyType> u(&myType); //unique_ptr<MyType> u(&myType, null_deleter<MyType>()); u.get_deleter() = null_deleter<MyType>(); return 0; }
运行报错:
Program returned: 139
Program stderr
free(): invalid pointer
原因分析与解决方案
核心原因
- 对象切片导致多态失效:
std::unique_ptr存储的是deleter<T>类型的实体对象,而非指针或引用。当你执行u.get_deleter() = null_deleter<MyType>()时,会发生对象切片——派生类null_deleter的特化部分被丢弃,仅保留基类deleter的内容,因此运行时调用的还是基类的operator()。 - 内存释放方式不匹配:你将栈上的
MyType myType地址传给unique_ptr,但默认deleter用free()释放栈内存,这本身就是非法操作——栈内存由系统自动管理,不能用free()手动释放,这也是free(): invalid pointer的直接触发原因。
解决方案
方案1:用多态指针实现动态切换删除器
要让多态生效,需将unique_ptr的删除器类型改为指针或智能指针,通过基类指针指向派生类对象:
#include <iostream> #include <memory> class MyType{ public: ~MyType() { std::cout<<"Destructor called\n"; } }; template<typename T> struct deleter { virtual ~deleter() = default; // 必须添加虚析构,避免派生类内存泄漏 virtual void operator()(T* p) const { std::cout<<"deleter called\n"; if (p) { p->~T(); free(const_cast<std::remove_const_t<T>*>(p)); } } }; template<typename T> struct null_deleter : public deleter<T> { void operator()(T* ptr) const override { std::cout<<"null deleter called\n"; } }; // 修改unique_ptr的删除器类型为shared_ptr<deleter<T>>,利用多态 template<typename T> using unique_ptr = std::unique_ptr<T, std::shared_ptr<deleter<T>>>; int main() { MyType myType; std::cout<<"hello\n"; // 初始化时传入null_deleter的shared_ptr unique_ptr<MyType> u(&myType, std::make_shared<null_deleter<MyType>>()); return 0; }
方案2:直接指定删除器类型(无需多态)
如果不需要动态切换删除器,直接在unique_ptr中指定具体删除器类型即可:
方式A:固定删除器别名
// 直接用null_deleter作为删除器类型 template<typename T> using unique_null_ptr = std::unique_ptr<T, null_deleter<T>>; int main() { MyType myType; unique_null_ptr<MyType> u(&myType); return 0; }
方式B:创建时显式传入删除器
int main() { MyType myType; std::unique_ptr<MyType, null_deleter<MyType>> u(&myType); return 0; }
额外注意事项
- 禁止让
unique_ptr管理栈对象!栈对象会在作用域结束时自动销毁,unique_ptr再次释放会导致双重销毁或非法内存操作,必须配合null_deleter使用。 - 使用多态删除器时,基类必须声明虚析构函数,否则销毁智能指针时只会调用基类析构,导致派生类资源泄漏。
内容的提问来源于stack exchange,提问作者Vivek Mangal
相关产品推荐
相关产品推荐

