You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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


原因分析与解决方案

核心原因

  1. 对象切片导致多态失效:std::unique_ptr存储的是deleter<T>类型的实体对象,而非指针或引用。当你执行u.get_deleter() = null_deleter<MyType>()时,会发生对象切片——派生类null_deleter的特化部分被丢弃,仅保留基类deleter的内容,因此运行时调用的还是基类的operator()。
  2. 内存释放方式不匹配:你将栈上的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 06:34:56