加载中…
个人资料
南冠彤
南冠彤
  • 博客等级:
  • 博客积分:0
  • 博客访问:415,028
  • 关注人气:59
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
相关博文
推荐博文
谁看过这篇博文
加载中…
正文 字体大小:

(转)boost智能指针shared_ptr使用要注意的几个问题

(2011-05-25 12:01:12)
标签:

杂谈

分类: CPP/C

boost库中的shared_ptr是个好东西,一度我认为可以像C#那样使用对象,完全不用考虑内存的释放问题。不过使用起来发现还是有许多要注意的地方。

首先,绝对不可以像这样使用:


view plaincopy to clipboardprint?
int *p = new int (10);  
boost::shared_ptr<int> sp1 (p);  
boost::shared_ptr<int> sp2 (p); 
int *p = new int (10);
boost::shared_ptr<int> sp1 (p);
boost::shared_ptr<int> sp2 (p);

这个看上去很简单,一般都不会这样犯错误。但如果在一个复杂的场景中呢?比如这样:
view plaincopy to clipboardprint?
class CListener  
 
public:  
    virtual void TakeAction () = 0;  
};  
class CPublisher  
 
public:  
    CPublisher ()  
    
        cout << "CPublisher::CPublisher()" << endl;  
    
    ~CPublisher ()  
    
        cout << "CPublisher::~CPublisher()" << endl;  
    
    void SetListener (boost::shared_ptr<CListener> listener)  
    
        m_listener = listener;  
        m_listener->TakeAction ();  
    
private:  
    boost::shared_ptr<CListener> m_listener;  
};  
class CConcreteListener : public CListener  
 
public:  
    CConcreteListener (const boost::shared_ptr<CPublisher> publiser)  
    
        cout << "CConcreteListener::CConcreteListener ()" << endl;  
        m_publisher = publiser;  
        m_publisher->SetListener (boost::shared_ptr<CListener> (this));  
    
    ~CConcreteListener ()  
    
        cout << "CConcreteListener::~CConcreteListener()" << endl;  
    
    void TakeAction ()  
    
        cout << "CConcreteListener::TakeAction()" << endl;  
    
private:  
    boost::shared_ptr<CPublisher> m_publisher;  
};  
void shared_ptr_test()  
 
    boost::shared_ptr<CPublisher> publisher1 (new CPublisher);  
    boost::shared_ptr<CConcreteListener> concreteListener1 (new CConcreteListener (publisher1));  

class CListener
{
public:
    virtual void TakeAction () = 0;
};
class CPublisher
{
public:
    CPublisher ()
    {
        cout << "CPublisher::CPublisher()" << endl;
    }
    ~CPublisher ()
    {
        cout << "CPublisher::~CPublisher()" << endl;
    }
    void SetListener (boost::shared_ptr<CListener> listener)
    {
        m_listener = listener;
        m_listener->TakeAction ();
    }
private:
    boost::shared_ptr<CListener> m_listener;
};
class CConcreteListener : public CListener
{
public:
    CConcreteListener (const boost::shared_ptr<CPublisher> publiser)
    {
        cout << "CConcreteListener::CConcreteListener ()" << endl;
        m_publisher = publiser;
        m_publisher->SetListener (boost::shared_ptr<CListener> (this));
    }
    ~CConcreteListener ()
    {
        cout << "CConcreteListener::~CConcreteListener()" << endl;
    }
    void TakeAction ()
    {
        cout << "CConcreteListener::TakeAction()" << endl;
    }
private:
    boost::shared_ptr<CPublisher> m_publisher;
};
void shared_ptr_test()
{
    boost::shared_ptr<CPublisher> publisher1 (new CPublisher);
    boost::shared_ptr<CConcreteListener> concreteListener1 (new CConcreteListener (publisher1));
}


在这里,我考虑设计一个简单的发布/定购的模式,为了简化内存管理,应用了shared_ptr。看上去好像没什么问题,
但实际并非如此,当shared_ptr_test()执行结束时会报错:

"Unhandled exception at ...: 0xC0000005: Access violation writing location 0xfeeefef2."

为什么?
这个错误太隐蔽了,问题出现在CConcreteListener的构造函数中。
这两行代码:
boost::shared_ptr<CConcreteListener> concreteListener1 (new CConcreteListener (publisher1));
和CConcreteListener构造函数中的
m_publisher->SetListener (boost::shared_ptr<CListener> (this));

如果展开用另一种方式看的话,相当于这样:
CConcreteListener *p1 = new CConcreteListener;
boost::shared_ptr<CListener> sp1 (p1);
publisher1->SetListener (sp1);
boost::shared_ptr<CConcreteListener> concreteListener1 (p1);

哦,看上去和上面那个例子犯了同样的错误,不过这里更隐蔽,更不容易发现。

那么我们改动一下,把
m_publisher->SetListener (boost::shared_ptr<CListener> (this));
从CConcreteListener的构造函数中移出去,然后在shared_ptr_test()中这样使用:

view plaincopy to clipboardprint?
CConcreteListener (const boost::shared_ptr<CPublisher> publiser)  
    
        cout << "CConcreteListener::CConcreteListener ()" << endl;  
        m_publisher = publiser;  
        //m_publisher->SetListener (boost::shared_ptr<CListener> (this));  
    
//......  
void shared_ptr_test()  
 
    boost::shared_ptr<CPublisher> publisher1 (new CPublisher);  
    boost::shared_ptr<CConcreteListener> concreteListener1 (new CConcreteListener (publisher1));  
    publisher1->SetListener (concreteListener1);  

CConcreteListener (const boost::shared_ptr<CPublisher> publiser)
    {
        cout << "CConcreteListener::CConcreteListener ()" << endl;
        m_publisher = publiser;
        //m_publisher->SetListener (boost::shared_ptr<CListener> (this));
    }
//......
void shared_ptr_test()
{
    boost::shared_ptr<CPublisher> publisher1 (new CPublisher);
    boost::shared_ptr<CConcreteListener> concreteListener1 (new CConcreteListener (publisher1));
    publisher1->SetListener (concreteListener1);
}

也就是在外面显式调用SetListener,恩,这回上面那个问题消失了,程序不会崩溃了。
这回正确了?看看程序的输出把:
CPublisher::CPublisher()
CConcreteListener::CConcreteListener ()
CConcreteListener::TakeAction()

好像有点问题,shared_ptr_test()函数已经结束了,publisher1和concreteListener1都已经过了生命期,应该析构销毁了,
那他们内部的的CPublisher和CConcreteListener对象也应该被释放呀,但没看到这两个类的析构函数被调用。

如果跟踪函数的调用,会发现当publisher1和concreteListener1结束生命期析构后,内部的引用计数都是1,导致内部引用对象无法释放。
这是由于两个类CPublisher和CConcreteListener中存在循环引用。
由于shared_ptr是通过引用计数管理对象,他是无法处理循环引用的问题,导致有些对象永远无法从内存中释放。
所以,使用shared_ptr,还必须小心循环引用的问题。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/hongjiqin/archive/2009/08/04/4406805.aspx

0

阅读 评论 收藏 转载 喜欢 打印举报/Report
  • 评论加载中,请稍候...
发评论

    发评论

    以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

      

    新浪BLOG意见反馈留言板 电话:4000520066 提示音后按1键(按当地市话标准计费) 欢迎批评指正

    新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

    新浪公司 版权所有