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

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

条款3:不要让shared_ptr的循环引用.

//---------------------------------------------
class Dog;
class Animal{
public:
    ~Animal(){
        cout << "Animal Deconstructor" << endl;
    }
    shared_ptr<Dog> _dog;
};
//---------------------------------------------
class Dog{
public:
    ~Dog(){
        cout << "Dog Deconstructor" << endl;
    }
    shared_ptr<Animal> _animal;
};

//---------------------------------------------

int main(){

    shared_ptr<Dog> dog = make_shared<Dog>();
    shared_ptr<Animal> animal = make_shared<Animal>();

    dog->_animal = animal;
    animal->_dog = dog;

    cout << "--------------------------" << endl;

    ;
}

这样 Animal 和 Dog 都不会被回收.

解决办法,将 shared_ptr<Dog> _dog  或   shared_ptr<Animal> _animal 其中的一个换成  weak_ptr.

条款4:不要在函数实参里创建shared_ptr

function ( shared_ptr<int>(new int), g( ) );  //有缺陷
可能的过程是先new int,然后调g( ),g( )发生异常,shared_ptr<int>没有创建,int内存泄露
 
shared_ptr<int> p(new int());
f(p, g());  //Boost推荐写法
 
条款5:对象内部生成shared_ptr
 
前面说过,不能把this指针直接扔给shared_ptr. 但是没有禁止在对象内部生成自己的shared_ptr
 
//这是Boost的例子改的。
class Y: public boost::enable_shared_from_this<Y>
{
    boost::shared_ptr<Y> GetSelf()
    {
        return shared_from_this();
    }
};
 
原理是这样的。普通的(没有继承enable_shared_from_this)类T的shared_ptr<T> p(new T).
p作为栈对象占8个字节,为了记录(new T)对象的引用计数,p会在堆上分配16个字节以保存
引用计数等“智能信息”。share_ptr没有“嵌入(intrusive)”到T对象,或者说T对象对share_ptr毫不知
 
情。Y对象则不同,Y对象已经被“嵌入”了一些share_ptr相关的信息,目的是为了找到“全局性”的
那16字节的本对象的“智能信息”。
 
原理说完了,就是陷阱
Y y;
boost::shared_ptr<Y> p=  y.GetSelf(); //无知的代码,y根本就不是new出来的
 
Y* y = new Y;
boost::shared_ptr<Y> p=  y->GetSelf(); //似是而非,仍旧程序崩盘。
Boost文档说,在调用shared_from_this()之前,必须存在一个正常途径创建的shared_ptr
 
boost::shared_ptr<Y> spy(new Y)
boost::shared_ptr<Y> p =  spy->GetSelf(); //OK
 
条款6 :处理不是new的对象要小心。
 
int* pi = (int*)malloc(4)
shared_ptr<int> sp( pi ) ; //delete马嘴不对malloc驴头。
 
条款7:多线程对引用计数的影响。
 
如果是轻量级的锁,比如InterLockIncrement等,对程序影响不大
如果是重量级的锁,就要考虑因为share_ptr维护引用计数而造成的上下文切换开销。
1.33版本以后的shared_ptr对引用计数的操作使用的是Lock-Free(类似InterLockIncrement函数族)
的操作,应该效率不错,而且能保证线程安全(库必须保证其安全,程序员都没有干预这些隐藏事物的机会)。
Boost文档说read,write同时对shared_ptr操作时,行为不确定。这是因为shared_ptr本身有两个成员px,pi。
多线程同时对px读写是要出问题的。与一个int的全局变量多线程读写会出问题的原因一样。
条款8:对象数组用shared_array
 
int* pint = new int[100];
shared_array<int> p (pint );
 
既然shared_ptr对应着delete;显然需要一个delete[]对应物shared_array
 
条款9:学会用删除器
 
struct Test_Deleter
{   
    void  operator ()( Test* p){   ::free(p);   }
};
Test* t = (Test*)malloc(sizeof(Test));
new (t) Test;
 
shared_ptr<Test> sp( t ,  Test_Deleter() ); //删除器可以改变share_ptr销毁对象行为
 
有了删除器,shared_array无用武之地了。
template<class T>
struct Array_Deleter
{   
    void  operator ()( T*){   delete[] p;   }
};
int* pint = new int[100];
shared_ptr<int> p (pint, Array_Deleter<int>() );
 
条款10:学会用分配器
 
存放引用计数的地方是堆内存,需要16-20字节的开销。
如果大量使用shared_ptr会造成大量内存碎片。
shared_ptr构造函数的第3个参数是分配器,可以解决这个问题。
 
shared_ptr<Test> p( (new Test), Test_Deleter(), Mallocator<Test>() );
注意删除器Test_Deleter是针对Test类的。分配器是针对shared_ptr内部数据的。
 
Mallocator<Test>()是个临时对象(无状态的),符合STL分配器规约。
 
template <typename T> 
class Mallocator { 
    //略。。。。。。
    T * allocate(const size_t n) const {
        return singleton_pool<T,sizeof(T)>::malloc();
    }
    //略。。。。。。
 
Mallocator传入Test,实际分配的类型确是
class boost::detail::sp_counted_impl_pda<class Test *,
                                         struct Test_Deleter,
                                         class Mallocator<class Test> >
这是用typeid(T).name()打印出来的。可能和rebind相关。
 
条款11 weak_ptr在使用前需要检查合法性。
weak_ptr<K> wp;
{
shared_ptr<K>  sp(new K);  //sp.use_count()==1
wp = sp; //wp不会改变引用计数,所以sp.use_count()==1
shared_ptr<K> sp_ok = wp.lock(); //wp没有重载->操作符。只能这样取所指向的对象
}
shared_ptr<K> 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<T>
 
本来shared_ptr就是为了管理指针资源的,不要又引入一个需要管理的指针资源shared_ptr<T>*
 
条款13  尽量不要get
 
class B{...};
class D : public B{ ...};  //继承层次关系
 
shared_ptr<B> sp (new D);    //通过隐式转换,储存D的指针。
B* b = sp.get();             //shared_ptr辛辛苦苦隐藏的原生指针就这么被刨出来了。
D* d = dynamic_cast<D*>(b);  //这是使用get的正当理由吗?
 
正确的做法
shared_ptr<B> spb (new D)  ;
shared_ptr<D> spd = shared_dynamic_cast<D>(spb); //变成子类的指针
shared_ptr在竭尽全力表演的像一个原生指针,原生指针能干的事,它也基本上能干。
 
另一个同get相关的错误
shared_ptr<T> sp(new T);
shared_ptr<T> sp2( sp.get() ) ;//又一个“二龙治水”实例,指针会删2次而错误。
 
条款14 不要memcpy shared_ptr
 
shared_ptr<B> sp1 (new B)  ;
shared_ptr<B> sp2;
memcpy(&sp2,&sp1,sizeof(shared_ptr<B>)); //sp2.use_count()==1
很显然,不是通过正常途径(拷贝构造,赋值运算),引用计数是不会正确增长的。
 
条款15 使用BOOST预定义的宏去改变shared_ptr行为。
 
shared_ptr行为由类似BOOST_SP_DISABLE_THREADS这样的宏控制。需要去学习他们到底是干什么的。
大师Andrei Alexandrescu设计了一种基于模板策略设计模式的智能指针,通过几个模板参数去定制化
智能指针的行为。Boost却不以为然,官方解释是:需要统一的接口,这样利于大规模书写。
smart_ptr<T,OwnershipPolicy,ConversionPolicy,CheckingPolicy,StoragePolicy> sp(new T);
上述接口缺点是外形复杂,看上去像个大花脸。优点是客户程序员可以轻易的定制行为。
 
条款17 构造函数里调用shared_from_this抛例外
 
class Holder:public enable_shared_from_this<Holder>{
public:
    Holder() {
        shared_ptr<Holder> 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共存就把人搞糊涂了。

内容转载自: http://blog.sina.com.cn/s/blog_62b4e3ff0100v1tc.html

最新文章

  1. windows或mac上对iOS设备截图
  2. 修改Android系统属性SystemProperties.set(&quot;sys.powerctl&quot;, &quot;shutdown&quot;)关机分析
  3. Firebird数据库的Select语句
  4. iOS开发--二维码的生成
  5. Swig 使用指南
  6. JAVA事务
  7. IE取消访问剪贴板设置
  8. com.transfer.www
  9. cefSharp 设置运行时系统语言
  10. ashx的学习
  11. vue学习笔记(四)——Vue实例以及生命周期
  12. Unity程序们经常用到的网址(方便自己用,一直更新)
  13. 用简单的C语言实现多任务轮流切换(模拟操作系统线程机制)
  14. 解释器模式 Interpreter 行为型 设计模式(十九)
  15. 【JavaScript】for循环小练习
  16. Css新增内容
  17. echarts双y轴折线图柱状图混合实时更新图
  18. 如何判断mac地址时multicast还是broadcast ?
  19. springboot+shiro+redis(单机redis版)整合教程-续(添加动态角色权限控制)
  20. hostname命令

热门文章

  1. XE 的程序升级 XE5 问题处理记录
  2. ACM/ICPC 之 最短路径-dijkstra范例(ZOJ2750-POJ1135(ZOJ1298))
  3. nyoj756_重建二叉树_先序遍历
  4. 【XLL 文档翻译】【第1部分】 Add-in 管理器 和 XLL 接口函数
  5. pod install 慢
  6. express响应前端ajax请求
  7. 实现iOS前台时的推送弹窗效果
  8. shared_ptr 和 unique_ptr
  9. python中使用sub替换字符串中的元素
  10. Label Font 字体样式设置