注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

c++的智能指针之二——智能指针的安全使用  

2014-02-05 19:47:52|  分类: C++(VC)编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

c++的智能指针之二——智能指针的安全使用

 

今天有些时间,总结了一下智能指针的几个安全使用的注意点,虽然有些已经重复过了,但是还是在这里详细的列一下,更清楚一些:

1、智能指针的线程安全性指的是智能指针对象本身,不是指智能指针指向的内容,这个一定要切记。

2、所谓“如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁(例3~5)。”是指针同一个指针智能指针在多线程中读写是不安全的。其实是和多线程同时读写一个共享变量是一致的结果。这个前几面几篇博文中有例程,这里不详述,很简单。

3、所谓智能指针在多线程下是安全的,是指智能指针在多个线程中保有多个复制体,或者说“共享引用计数器”的一个智能指针的多个复制对象是安全的。

注意:23主要是在陈硕的“为什么多线程读写 shared_ptr 要加锁?[http://blog.csdn.net/solstice/article/details/8547547]”这篇BLOG的引导下进行总结的。

4、对于重复引用同一个实体对象创建智能指针的错误:

 shared_ptr多次引用同一数据,如下:

XXX函数()

{

 

int* pInt = new int[100];

 

boost::shared_ptr<int> sp1(pInt);

 

// 一些其它代码之后…

 

boost::shared_ptr<int> sp2(pInt);

 

}

说明:这是不正确的,会引起二次释放

 

 

5、父子类的智能指针。即支持多态,可以在父智能指针中插入子类对象。

6、循环引用,一般是子类持有父类的weak_ptr ,父类持有子类的shared_ptr

http://blog.csdn.net/liuzhi1218/article/details/6993135

最后值得一提的是,虽然通过弱引用指针可以有效的解除循环引用,但这种方式必须在程序员能预见会出现循环引用的情况下才能使用,也可以是说这个仅仅是一种编译期的解决方案,如果程序在运行过程中出现了循环引用,还是会造成内存泄漏的。因此,不要认为只要使用了智能指针便能杜绝内存泄漏。毕竟,对于C++来说,由于没有垃圾回收机制,内存泄漏对每一个程序员来说都是一个非常头痛的问题。

http://blog.sina.com.cn/s/blog_48d4cf2d0100uhgt.html

一个例子,假设我们要实现一个双向链表。(这里只是举个例子。链表这种结构效率很重要又很基本,当然应该使用原生指针。)如果结点的prevnext都使用strong智能指针的话,则引用计数(中括号内的数字)是这样:  head -> Node1[2] <-> Node2[2] <-> Node3[1]。当链表析构函数让head不再指向Node1后则引用计数变成这样: Node1[1] <-> Node2[2] <-> Node3[1]。发生内存泄露了。如果我们规定prev使用weak_ptr。则一开始引用计数是这样: head -> Node1[1] <-> Node2[1] <-> Node3[1]。这样当我们让head不再指向Node1的时候将级联删除所有结点。

    看起来很美好,但为何我反倒是建议慎用weak_ptr呢?这得回到实际编程中为何存在循环引用这个问题上来。为何两个对象要互相引用对方?我发现在绝大多数情况下几乎总是因为两个对象存在类似整体/部分的这种关系(要从广义角度理解整体-部分)。不管是否使用weak_ptr,甚至不管是否使用智能指针,都需要开发者正确识别出这种关系。使用weak_ptr,一般是让整体对象持有部分对象的strong ptr,部分持有整体的weak ptr这样当然是ok。但我更喜欢让整体对象持有部分对象的strong ptr,部分对象则直接持有整体对象的原生指针甚至是引用(一般来说引用更好,要求构造部分对象的时候传入整体对象作为参数),然后保证整体对象的生命周期涵盖部分对象的生命周期。这两种方法相比,后者效率高(原生指针vs对象),编程简单(使用时无需提升+判断成败)。当然如果违背了整体对象生命周期涵盖部分对象生命周期的原则,会死得比较惨。但是我觉得大部分时候这是好事,因为符合Die fast Die fierce 原则。使用weak_ptr,一团和气之下可能掩盖了错误的对象析构顺序,而这可能又是由其他逻辑错误导致的。

7、需要说明的是:析构函数的过程一般认为是写入。

8、智能指针的构造方式除了前文说到的三种方法还有:

       shared_ptr<tester> sget()

       {

                 //shared_ptr<tester> t = shared_ptr<tester>(new tester());注意这里,

                 //shared_ptr<tester> tt(new tester());

                 //return tt;

                 //直接返回THIS会引起二次释放,崩溃。解决方法是使用class tester : public boost::enable_shared_from_this<tester>

           return shared_ptr<tester> (this);注意这里

       }上面两个注意的地方与一般的使用不同。而且在返回值中直接返回带变量定义是非法的。

做为返回值,或者如注释的第一行的方法。

9、对于返回this指针的方法:

直接返回肯定是错误的,原理同4,二次释放,即如下情况:

解决方法是使用BOOST或者C++11库中提供的:

 

enable_shared_from_this

http://www.cnblogs.com/hujian/archive/2012/12/10/2810754.html

   另外,使用shared_ptr来包装this时,也会产生与上面类似的问题。考虑如下代码:

 1 class A

 2 {

 3 public:

 4         shared_ptr<A> Get()

 5         {

 6              return shared_ptr<A>(this);

 7         }

 8 }

 9

10 shared_ptr<A> pA(new A());

11 shared_ptr<A> pB = pA->Get();   

pApB离开作用域时,会将堆上的对象释放两次。如何解决上述问题呢?C++ 11提供了如下机制:将类从enable_shared_from_this类派生,获取shared_ptr时使用shared_from_this接口。参考代码如下:

1 class A public enable_shared_from_this<A>

2 {

3 public:

4         shared_ptr<A> Get()

5         {

6              return shared_from_this();

7         }

8 }   

10、在多线程中使用shared_ptr时,如果存在拷贝或赋值操作,可能会由于同时访问引用计数而导致计数无效。解决方法是向每个线程中传递公共的week_ptr,线程中需要使用shared_ptr时,将week_ptr转换成shared_ptr即可。

 

这里其实有一点原来是理解错误的,就是在“C++中的智能指针”

http://fpcfjf.blog.163.com/blog/static/5546979320136244734874/

中对本文中上面的4中的重复引用和5中对父子类对象的使用的例子给理解错了。当时也有些迷惑,没有求甚解,以后要注意。

三国志上说诸葛亮,好读书,不求甚解。搞技术可不行啊。

  评论这张
 
阅读(832)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017