C++\CLIC++ 2a

来自std::shared_ptr的问题,不仅仅是循环引用

2022-09-05  本文已影响0人  左图右码

《C++标准库,2nd》用father/mother/kids的例子揭示了shared_ptr循环引用所导致的析构失败的问题,见5.2.2。这最多造成memory leak,更致命的是造成app崩溃。如我所遇:

struct displayElementPts
{
    static std::shared_ptr< displayElementPts> instance(){...}
    void clear(){...}
}

class dialog{
    static std::shared_ptr< dialog > instance(){...}
    virtual void closeEvent(...){
      auto dis = displayElementPts::instance();
      dis->clear();
    }
}

在dialog关闭的时候,会调用displayElementPts。
一般这是无害的。但退出app的时候,对话框会调用closeEvent ,问题就在这里,如果这个对话框从来就没实例化过,就没问题,如果正在显示,此时退出app,对话框会销毁,并调用closeevent。dialog与displayElementPts一样,也是单例方式创建的实例,由一个静态的std::shared_ptr拥有着,他们的析构次序是不确定的,所以程序会在退出的时候发生随机的崩溃。

轮到std::weak_ptr了,你虽不怎么关注它,或者几乎没用过它,但它并不像名字所示的那么弱。

class dialog{
    static std::shared_ptr< dialog > instance(){...}
    virtual void closeEvent(...){
      if(!m_wp.expired())
          m_wp->locked()->clear();
    }
dialog()
{
    //...
    m_wp = displayElementPts::instance();
}
private:
  std::weak_ptr< displayElementPts > m_wp;
}

先用expired()判断单实例是否有效,再用locked取得有效的指针。就能避免获取或者不能获取有效实例的问题。

有没有带着镣铐跳舞的感觉,其实不然,只是在特殊的事件里才需要判断,其它的位置,直接调用locked就好了。

请安心享用std::shared_ptr吧。

上一篇下一篇

猜你喜欢

热点阅读