ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

enable_shared_from_this:类内部安全拿到 shared_ptr

enable_shared_from_this:类内部安全拿到 shared_ptr 在类的成员函数里,有时需要把「指向自己的shared_ptr」交给别人——把自己注册进容器、挂到回调里、塞进观察者列表。最危险的写法是直接std::shared_ptrT(this):它编译通过、看着能用,实则埋下double free(重复释放)的雷。这篇把雷挖出来,并给出正确解法std::enable_shared_from_this(启用共享自引用,enable shared-from-this)。1. 引子:直接 shared_ptr(this) 造出第二个控制块this是个裸指针。从它再构造一个shared_ptr,库根本不知道「这个对象早就被别的shared_ptr管着」——它会新建一个独立的控制块。两个控制块都以为自己独占这个对象,析构时各delete一次,同一个对象就被释放了两遍:// 反例片段(无 main(),不参与编译校验):两个控制块,同一个对象#includememorystructFoo{~Foo(){/* 清理逻辑 */}};voidbad(std::shared_ptrFooowner){std::shared_ptrFooself(owner.get());// 反例,不要这么写:从裸指针新建了第二个控制块// owner 和 self 各有各的控制块、各算各的计数,// 退出时两个控制块都会 delete 同一个 Foo - double free}不用等到运行,编译器就看出不对劲了。GCC 13 在-O2下的真实警告(节选,去掉了头文件路径):warning: 'void operator delete(void*, std::size_t)' called on pointer 'unknown' with nonzero offset 16 [-Wfree-nonheap-object]根因就是这张图:错误:两个控制块(double free 的根源) a ── [控制块#1: 强=1] ┐ ├── [同一个对象 Foo] b ── [控制块#2: 强=1] ┘ (退出时两个控制块各 delete 一次 - 崩溃) 正确:一个控制块(enable_shared_from_this) a ── [控制块: 强=1, 弱=1] ── [对象 Foo] ↑ shared_from_this() 返回的是同一控制块上的别名, 只把强计数 +1,绝不新建控制块官方文档:std::shared_ptr 的构造函数(含从裸指针构造)— cppreference2. 正确解法:继承 enable_shared_from_this让类继承std::enable_shared_from_thisT,内部用shared_from_this()拿到的就是和原shared_ptr共享同一个控制块的指针:// esft_ok.cpp — 编译: g++ -std=c++17 -Wall -O2 esft_ok.cpp -o esft_ok#includeiostream#includememoryclassNode:publicstd::enable_shared_from_thisNode{public:voidregister_self(){conststd::shared_ptrNodeself=shared_from_this();// 安全:共享同一控制块std::cout"use_count = "self.use_count()'\n';}~Node
返回列表