慎用智能指针的reset方法

发布时间 2023-10-21 11:31:38作者: azureology

背景

使用智能指针指向class的成员变量会导致指针Segmentation fault.

复现

直接看代码https://godbolt.org/z/Tnx45jraP

#include <iostream>
#include <memory>

struct Handler
{
    int num = 7;
};

int main()
{
    Handler handler;
    std::shared_ptr<int> ptr = nullptr;
    ptr.reset(&handler.num);
    std::cout<< *ptr << std::endl;
    return 0;
}

运行结果

ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 139
free(): invalid pointer
Program terminated with signal: SIGSEGV
7

分析

指针的值成功打印后发生错误,换成引用就没问题。
使用raw pointer int*也没问题,看来问题出在shared_ptr::reset()方法上。

template< class Y, class Deleter, class Alloc >
void reset( Y* ptr, Deleter d, Alloc alloc );

根据定义DeleterAlloc可以不传,其中Deleter会采取默认行为free()
而智能指针所管理的handler.numhandler所拥有
handler析构时也会尝试释放成员变量num最终导致double free问题。

解决

知道原因,可以想到如下解决方案:

  • 使用raw pointer代替智能指针阻止析构发生
  • 手动传入deleter方法阻止智能指针析构所指对象
    这里提供第二种代码案例https://godbolt.org/z/Kfee11qqW
#include <iostream>
#include <memory>

struct Handler
{
    int num = 7;
};

void D(void * p)
{
    std::cout<< "This deleter does nothing." << std::endl;
}

int main()
{
    Handler handler;
    std::shared_ptr<int> ptr = nullptr;
    ptr.reset(&handler.num, D);
    std::cout<< *ptr << std::endl;
    return 0;
}

参考

std::shared_ptr::reset - cppreference.com