std::enable_shared_from_this

std::enable_shared_from_this 是一个模板类,用于赋予继承它的类能够从其成员函数中安全地生成该对象的 std::shared_ptr。当你有一个由 std::shared_ptr 管理的对象,并希望在该对象的成员函数内部获得一个指向自身的 std::shared_ptr 时,std::enable_shared_from_this 就非常有用。这在异步操作、事件处理回调或任何需要延长对象生命周期以等待某些操作完成的场景中特别有价值。

值得注意的一点是shared_ptr可以直接用指向对象的指针来构造,

但是不推荐这样,因为如果同时声明了两个这样的shared_ptr就会造成对同一空间的重复删除。

那么我们在遇到一个希望从自身复制出 shared_ptr 的情况的时候,就可能会遇到下面的代码。

#include <memory>
#include <iostream>

class BadExample : public std::enable_shared_from_this<BadExample> {
public:
    //Example1
    void doSomething() {
        // 错误使用:直接使用this创建一个新的std::shared_ptr
        std::shared_ptr<BadExample> ptr(this);
        // 做一些事情...
    }

    //Example2
    std::shared_ptr<BadExample> get_shared_ptr() {return std::shared_ptr<BadExample>(this);}  

    ~BadExample() {
        std::cout << "BadExample destroyed\n";
    }
};

int main() {
    // 正确创建std::shared_ptr
    std::shared_ptr<BadExample> correct_ptr = std::make_shared<BadExample>();
    //Example1
    // 调用成员函数,其中错误地尝试使用this初始化std::shared_ptr
    correct_ptr->doSomething();
    //Example2
    //每次调用 get_shared_ptr() 时,都会创建一个新的std::shared_ptr实例,该实例有自己的控制块
    std::shared_ptr<BadExample> correct_ptr1 = correct_ptr->get_shared_ptr();
    // 当main退出时,correct_ptr被销毁,BadExample对象被删除
    // 但因为doSomething中创建了一个独立的控制块,尝试再次删除对象,导致未定义行为
    return 0;
}

使用裸 this 指针来初始化或赋值给一个 std::shared_ptr 会导致多个独立的 std::shared_ptr 控制块被创建,这违反了 std::shared_ptr 的设计初衷——管理对象生命周期和所有权共享。每个控制块都会尝试独立管理对象的生命周期,导致对象可能会被多次删除,进而引发未定义行为,如双重删除(double delete)错误。

Example1:

在这个错误示范中,BadExample 类的 doSomething 方法试图使用 this 指针来创建一个新的 std::shared_ptr<BadExample> 实例。这会创建一个新的控制块来管理 BadExample 对象的生命周期,与 main 函数中通过 std::make_shared 创建的 std::shared_ptr 完全独立。结果,当 main 函数结束时,两个独立的 std::shared_ptr 都会尝试销毁同一个对象,可能导致程序崩溃或其他未定义行为。

正确的做法是使用 std::enable_shared_from_this 提供的 shared_from_this() 方法,它保证返回一个与当前对象共享所有权的 std::shared_ptr,而不会创建新的控制块:

void doSomething() {
    std::shared_ptr<BadExample> ptr = shared_from_this();
    // 使用ptr做一些事情...
}

Example2:

这段代码的问题在于,每次调用 get_shared_ptr() 时,都会创建一个新的 std::shared_ptr 实例,该实例有自己的控制块。如果对象最初是通过 std::make_shared 或被一个 std::shared_ptr 管理的,那么通过 this 直接创建的 std::shared_ptr 将会有一个完全独立的控制块。这意味着,原有的 std::shared_ptr 和新创建的 std::shared_ptr 不会共享引用计数,可能导致对象被提前销毁或者多次销毁。

正确的用法示范

std::shared_ptr<A> get_shared_ptr() {
        // 正确:使用shared_from_this()安全地获取指向*this的std::shared_ptr
        return shared_from_this();
    }

如何工作

  • 类继承自 std::enable_shared_from_this
  • 当一个对象被 std::shared_ptr 管理时,std::enable_shared_from_this 内部会存储一个指向该对象的弱引用(std::weak_ptr)。
  • 可以通过调用 shared_from_this() 成员函数来获取一个指向当前对象的 std::shared_ptr。这保证了返回的 std::shared_ptr 与最初管理对象的 std::shared_ptr 共享所有权和控制块。

使用场景和好处

  1. 异步操作:在异步操作中,如果你需要保证操作完成时对象仍然存活,可以通过 shared_from_this() 获取一个 std::shared_ptr,作为异步操作的一个参数传递给回调函数或绑定到回调函数上。这样,只要异步操作尚未完成,对象就不会被销毁。
  2. 事件处理:在事件驱动的设计中,如果对象需要在事件发生时处理这些事件,通过 shared_from_this() 获取的 std::shared_ptr 可以确保在处理事件的过程中对象保持有效。
  3. 避免悬挂指针:它帮助避免使用原始指针可能导致的悬挂指针问题,因为 shared_from_this() 确保只要有 shared_ptr 存在,对象就不会被意外销毁。

注意事项

  • 只有当至少有一个 std::shared_ptr 实例已经拥有对象的所有权时,调用 shared_from_this() 才是安全的。否则,尝试调用 shared_from_this() 会抛出 std::bad_weak_ptr 异常。
  • 因此,通常在对象的构造函数完成后,在外部通过 std::shared_ptr 管理对象时开始使用 shared_from_this()
  • 10
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值