c++11 shared_ptr陷阱

1260阅读 0评论2018-09-06 leiminchn
分类:C/C++

原文地址:http://blog.sina.com.cn/s/blog_62b4e3ff0100v1tc.html


条款1:不要把一个原生指针给多个shared_ptr管理
int* ptr = new int;
shared_ptr p1(ptr);
shared_ptr p2(ptr); //logic error
ptr对象被删除了2次
这种问题比喻成“二龙治水”,在原生指针中也同样可能发生。

条款2:不要把this指针给shared_ptr
class Test{
public:
    void Do(){  m_sp =  shared_ptr(this);  }
private:
    shared_ptr m_member_sp;
};

Test* t = new Test;
shared_ptr local_sp(t);
p->Do();

发生什么事呢,t对象被删除了2次!
t对象给了local_sp管理,然后在m_sp =  shared_ptr(this)这句里又请了一尊神来管理t。
这就发生了条款1里“二龙治水”错误。

条款3:shared_ptr作为被保护的对象的成员时,小心因循环引用造成无法释放资源。
对象需要相互协作,对象A需要知道对象B的地址,这样才能给对象B发消息(或调用其方法)。
设计模式中有大量例子,一个对象中有其他对象的指针。现在把原生指针替换为shared_ptr.

假设a对象中含有一个shared_ptr指向b对象;假设b对象中含有一个shared_ptr
class boost::detail::sp_counted_impl_pda                                          struct Test_Deleter,
                                         class Mallocator >
这是用typeid(T).name()打印出来的。可能和rebind相关。

条款11 weak_ptr在使用前需要检查合法性。
weak_ptr wp;
{
shared_ptr  sp(new K);  //sp.use_count()==1
wp = sp; //wp不会改变引用计数,所以sp.use_count()==1
shared_ptr sp_ok = wp.lock(); //wp没有重载->操作符。只能这样取所指向的对象
}
shared_ptr sp_null = wp.lock(); //sp_null .use_count()==0;
因为上述代码中sp和sp_ok离开了作用域,其容纳的K对象已经被释放了。
得到了一个容纳NULL指针的sp_null对象。在使用wp前需要调用wp.expired()函数判断一下。
因为wp还仍旧存在,虽然引用计数等于0,仍有某处“全局”性的存储块保存着这个计数信息。
直到最后一个weak_ptr对象被析构,这块“堆”存储块才能被回收。否则weak_ptr无法直到自己
所容纳的那个指针资源的当前状态。

条款12 不要new shared_ptr
本来shared_ptr就是为了管理指针资源的,不要又引入一个需要管理的指针资源shared_ptr*

条款13  尽量不要get
class B{...};
class D : public B{ ...};  //继承层次关系

shared_ptr sp (new D);    //通过隐式转换,储存D的指针。
B* b = sp.get();             //shared_ptr辛辛苦苦隐藏的原生指针就这么被刨出来了。
D* d = dynamic_cast(b);  //这是使用get的正当理由吗?

正确的做法
shared_ptr spb (new D)  ;
shared_ptr spd = shared_dynamic_cast(spb); //变成子类的指针
shared_ptr在竭尽全力表演的像一个原生指针,原生指针能干的事,它也基本上能干。

另一个同get相关的错误
shared_ptr sp(new T);
shared_ptr sp2( sp.get() ) ;//又一个“二龙治水”实例,指针会删2次而错误。

条款14 不要memcpy shared_ptr
shared_ptr sp1 (new B)  ;
shared_ptr sp2;
memcpy(&sp2,&sp1,sizeof(shared_ptr)); //sp2.use_count()==1
很显然,不是通过正常途径(拷贝构造,赋值运算),引用计数是不会正确增长的。

条款15 使用BOOST预定义的宏去改变shared_ptr行为。
shared_ptr行为由类似BOOST_SP_DISABLE_THREADS这样的宏控制。需要去学习他们到底是干什么的。
大师Andrei Alexandrescu设计了一种基于模板策略设计模式的智能指针,通过几个模板参数去定制化
智能指针的行为。Boost却不以为然,官方解释是:需要统一的接口,这样利于大规模书写。
smart_ptr sp(new T);
上述接口缺点是外形复杂,看上去像个大花脸。优点是客户程序员可以轻易的定制行为。

条款16 构造函数里调用shared_from_this抛例外
class Holder:public enable_shared_from_this{
public:
    Holder() {
        shared_ptr sp = shared_from_this();
        int x = sp.use_count();
    }
};
同前面条款5,不符合enable_shared_from_this使用前提。

总结:
学习了一天就总结出10多条条款,长期研究一下恐怕就出现条款100了。为什么还要使用shared_ptr呢?
有很多开源库用shared_ptr,而且shared_ptr具有“传染性”(某网友语:像毒品沾上就甩不掉),
抛开它就会有更严重的多龙治水现象。shared_ptr作为原生指针的替代品,能解决一定的内存泄露问题。
实际上初学原生指针时,每个人都遇到过野指针,删两次,忘记删除等问题。学习shared_ptr也会遇到。
shared_ptr的确能改善上述问题,并不能完全解决问题。shared_ptr可能在将来占主流,它最可能号令江湖,
否则一大堆auto_ptr,weak_ptr,原生指针,scoped_ptr共存就把人搞糊涂了。
上一篇:RFC3550 RTP 中文版
下一篇:AAC 格式分析