【C++】 enable_shared_from_this 用法

enable_shared_from_this 用法

enable_shared_from_this 是 C++ 标准库中的一个模板类,用于在类的成员函数内部安全地获取指向自身的 shared_ptr。为什么需要它?直接用 this 构造 shared_ptr 会导致双重释放:

class Foo {
public:
    std::shared_ptr<Foo> get() {
        return std::shared_ptr<Foo>(this); // ❌ 危险!产生第二个独立的引用计数
    }
};

auto p1 = std::make_shared<Foo>();
auto p2 = p1->get(); // p1 和 p2 各自独立管理同一块内存 → 析构时 double free

正确用法:

#include <memory>

class Foo : public std::enable_shared_from_this<Foo> {
public:
    std::shared_ptr<Foo> get() {
        return shared_from_this(); // ✅ 共享同一个引用计数
    }
};

auto p1 = std::make_shared<Foo>();
auto p2 = p1->get();

std::cout << p1.use_count(); // 2,引用计数正确共享

例子 1:HTTP Session(❌ 错误 vs ✅ 正确)

❌ 错误版本:对象提前析构,回调访问悬空指针

#include <iostream>
#include <memory>
#include <functional>
#include <thread>
#include <chrono>

// 模拟异步操作(比如网络库)
void async_read(std::function<void(std::string)> callback) {
    std::thread([cb = std::move(callback)]() {
        std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟 IO 延迟
        cb("received data");
    }).detach();
}

class Session {
public:
    Session(int id) : id_(id) {
        std::cout << "[Session " << id_ << "] 构造\n";
    }
    ~Session() {
        std::cout << "[Session " << id_ << "] 析构 ← 对象已销毁!\n";
    }

    void start() {
        // ❌ 直接捕获 this 裸指针
        async_read([this](std::string data) {
            // 100ms 后执行这里,但 Session 可能已经析构了!
            std::cout << "[Session " << id_ << "] 收到: " << data << "\n"; // 💥 UB!
        });
    }

private:
    int id_;
};

int main() {
    {
        auto session = std::make_shared<Session>(1);
        session->start();
    } // ← shared_ptr 离开作用域,Session 立刻析构

    std::this_thread::sleep_for(std::chrono::milliseconds(200));
    // 回调在这里执行,但 Session 已经没了 → 未定义行为
}

✅ 正确版本:shared_from_this 延长生命周期

#include <iostream>
#include <memory>
#include <functional>
#include <thread>
#include <chrono>

void async_read(std::function<void(std::string)> callback) {
    std::thread([cb = std::move(callback)]() {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        cb("received data");
    }).detach();
}

class Session : public std::enable_shared_from_this<Session> {
public:
    Session(int id) : id_(id) {
        std::cout << "[Session " << id_ << "] 构造\n";
    }
    ~Session() {
        std::cout << "[Session " << id_ << "] 析构\n";
    }

    void start() {
        // ✅ 捕获 shared_ptr,引用计数 +1,对象至少活到回调结束
        auto self = shared_from_this();
        async_read([self](std::string data) {
            std::cout << "[Session " << self->id_ << "] 收到: " << data << "\n"; // ✅ 安全
        });

        std::cout << "[Session " << id_ << "] start() 结束, use_count="
                  << self.use_count() << "\n"; // 此时 = 3(main里1个 + self + lambda捕获1个)
    }

private:
    int id_;
};

int main() {
    {
        auto session = std::make_shared<Session>(1);
        session->start();
        // start() 返回后,main 的 shared_ptr 是 use_count=2(lambda 里还有一个 self)
    } // ← main 的 shared_ptr 析构,use_count 变为 1,对象还活着!

    std::cout << "main: shared_ptr 已离开作用域,等待回调...\n";
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
    // 回调执行完毕,lambda 销毁,self 析构,use_count=0,Session 才真正析构
}

例子 2:链式异步操作(连续多个回调)

这个场景更真实:一个对象需要连续发起多个异步操作(读 → 处理 → 写),每一步都需要保证对象存活。

❌ 错误版本:用 new + 裸指针,生命周期完全失控

class Pipeline {
public:
    void run() {
        // 第一步:异步读
        async_step1([this]() {           // ❌ 裸 this
            process();
            async_step2([this]() {       // ❌ 裸 this,此时 this 可能已经没了
                write_result();
            });
        });
    }

    void process()      { /* ... */ }
    void write_result() { /* ... */ }  // 💥 可能访问已析构对象
};

// 调用方根本不知道对象何时能被安全删除
Pipeline* p = new Pipeline();
p->run();
delete p;  // ❌ 可能太早删了,也可能永远不删(内存泄漏)

✅ 正确版本:每一步都续命

#include <iostream>
#include <memory>
#include <functional>
#include <thread>
#include <chrono>

// 模拟三个异步步骤
void async_step(int delay_ms, std::function<void()> cb) {
    std::thread([=]() {
        std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
        cb();
    }).detach();
}

class Pipeline : public std::enable_shared_from_this<Pipeline> {
public:
    static std::shared_ptr<Pipeline> create(std::string name) {
        return std::shared_ptr<Pipeline>(new Pipeline(std::move(name)));
        // 注意:不能用 make_shared,因为构造函数是私有的
    }

    void run() {
        std::cout << name_ << " 开始运行, use_count=" << shared_from_this().use_count() << "\n";
        step1();
    }

private:
    explicit Pipeline(std::string name) : name_(std::move(name)) {}

    void step1() {
        auto self = shared_from_this(); // 引用计数 +1
        std::cout << name_ << " step1 发起, use_count=" << self.use_count() << "\n";

        async_step(50, [self]() {
            // ✅ self 保证 Pipeline 在这里仍然存活
            std::cout << self->name_ << " step1 完成\n";
            self->step2(); // 进入下一步
        });
    } // self 在 lambda 里,step1() 返回后对象不会析构

    void step2() {
        auto self = shared_from_this(); // 再次续命
        std::cout << name_ << " step2 发起, use_count=" << self.use_count() << "\n";

        async_step(50, [self]() {
            std::cout << self->name_ << " step2 完成\n";
            self->step3();
        });
    }

    void step3() {
        auto self = shared_from_this();
        std::cout << name_ << " step3 发起, use_count=" << self.use_count() << "\n";

        async_step(50, [self]() {
            std::cout << self->name_ << " step3 完成,Pipeline 结束\n";
            // lambda 销毁 → self 析构 → use_count=0 → Pipeline 析构
        });
    }

    std::string name_;
};

int main() {
    {
        auto p = Pipeline::create("MyPipeline");
        p->run();
    } // ← main 的 shared_ptr 析构,但 Pipeline 仍然活着(被各步骤的 self 持有)

    std::cout << "main: 已离开作用域,Pipeline 在后台继续运行...\n";
    std::this_thread::sleep_for(std::chrono::milliseconds(300));
}

输出:

MyPipeline 开始运行, use_count=2
MyPipeline step1 发起, use_count=3
main: 已离开作用域,Pipeline 在后台继续运行...
MyPipeline step1 完成
MyPipeline step2 发起, use_count=2
MyPipeline step2 完成
MyPipeline step3 发起, use_count=2
MyPipeline step3 完成,Pipeline 结束
[Pipeline 析构]                        ← 最后一个 self 销毁后才析构

 

posted @ 2018-05-09 16:19  苏格拉底的落泪  阅读(211)  评论(0)    收藏  举报