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

使用C++11智能指针实现观察者模式时程序崩溃求助

问题分析与解决方案

崩溃核心原因

你代码里的崩溃根源在于Observer构造函数中错误的shared_ptr转换:

subject->attach(static_cast<ObserverSPtr>(this));

这里直接将裸指针this强转为shared_ptr<iobserver>,会生成一个全新的智能指针控制块,和make_shared<Observer>创建实例时的控制块完全独立。这会导致同一个Observer对象被两个无关联的shared_ptr管理,当对象析构时,两个控制块都会尝试释放内存,触发重复释放崩溃;同时detach时,用外部的observer3(来自make_shared)去匹配Subject中存储的错误shared_ptr,根本找不到目标,后续操作也会引发异常。

另外,你给Subject添加enable_shared_from_this是多余的——崩溃和Subject的智能指针管理无关,问题出在Observer的shared_ptr传递上。

修复后的完整代码

#include <iostream>
#include <memory>
#include <list>

// 观察者接口
class iobserver
{
public:
    virtual void update() = 0;
    virtual ~iobserver() = default; // 虚析构,确保子类正确析构
};

using ObserverSPtr = std::shared_ptr<iobserver>;
using ObserverWPtr = std::weak_ptr<iobserver>;

// 主题接口
class isubject
{
public:  
    virtual void notifyall() = 0;
    virtual void attach(ObserverSPtr observer) = 0;
    virtual void detach(ObserverSPtr observer) = 0;

    virtual int& getState() = 0;
    virtual void setState(int state) = 0;
    virtual ~isubject() = default;
};

using SubjectSptr = std::shared_ptr<isubject>;

class Subject : public isubject
{
    int state;
    std::list<ObserverWPtr> subscribed_observers; // 改用弱引用存储观察者

public:
    Subject() : state(-1) {}

    void attach(ObserverSPtr observer) override
    {
        subscribed_observers.push_back(observer);
    }

    void detach(ObserverSPtr observer) override
    {
        // 遍历列表,找到对应观察者的弱引用并移除
        subscribed_observers.remove_if([&](const ObserverWPtr& wptr) {
            auto sptr = wptr.lock();
            return sptr && sptr == observer;
        });
    }

    void notifyall() override
    {
        // 遍历前复制列表,避免遍历过程中列表被修改(比如detach)导致迭代器失效
        auto observers_copy = subscribed_observers;
        for (auto& wptr : observers_copy)
        {
            auto sptr = wptr.lock();
            if (sptr) // 检查观察者是否还存活
            {
                sptr->update();
            }
            else
            {
                // 清理已失效的弱引用(可选,也可以定期清理)
                subscribed_observers.remove(wptr);
            }
        }
    }

    int& getState() override
    {
        return state;
    }

    void setState(int state) override
    {
       this->state = state;
       this->notifyall();
    }
};

class Observer : public iobserver, public std::enable_shared_from_this<Observer>
{
    std::weak_ptr<isubject> subject;
public:
    Observer(SubjectSptr sub) : subject(sub)
    {
        // 使用shared_from_this获取正确的shared_ptr,和make_shared创建的实例关联
        sub->attach(shared_from_this());
    }

    void update() override
    {
        auto sub_ptr = subject.lock();
        if (sub_ptr) // 检查主题是否存活
        {
            std::cout << "Subject got updated to new state:" << sub_ptr->getState() << std::endl;
        }
    }
};

// 驱动代码
int main()
{
    auto subject = std::make_shared<Subject>();
    auto observer1 = std::make_shared<Observer>(subject);
    auto observer2 = std::make_shared<Observer>(subject);
    auto observer3 = std::make_shared<Observer>(subject);
   
    subject->setState(2);
    std::cout << "移除observer3后:" << std::endl;
    subject->detach(observer3);
    subject->setState(5);

    return 0;
}

高效实现观察者模式的要点

  1. 正确使用enable_shared_from_this
    永远不要直接将裸指针转换为shared_ptr,必须通过shared_from_this()获取和实例关联的智能指针,避免控制块重复创建。

  2. 用weak_ptr存储观察者

    • 避免Subject延长观察者的生命周期,让观察者的生命周期由外部管理。
    • 防止潜在的循环引用(比如如果Subject也被观察者持有shared_ptr时)。
    • 通知前通过lock()检查观察者是否存活,避免访问已销毁的对象。
  3. 避免遍历过程中修改容器
    通知时先复制观察者列表,或者使用安全的迭代方式,防止detach操作导致迭代器失效。

  4. 添加虚析构函数
    接口类(iobserver、isubject)必须添加虚析构函数,确保子类对象通过基类指针被正确析构。

内容的提问来源于stack exchange,提问作者Meraj Hussain

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 01:09:56