条款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
- c++ shared_ptr 使用注意事项. 2
1.抛弃临时对象,让所有的智能指针都有名字. 2.类向外传递 this 的 shared_ptr 让类继承 enable_shared_from_this. 然后返回 shared_from_ ...
- share_ptr_c++11
C++智能指针 shared_ptr shared_ptr 是一个标准的共享所有权的智能指针, 允许多个指针指向同一个对象. 定义在 memory 文件中(非memory.h), 命名空间为 std. ...
- c++11特性
0. 简介 在c++11标准中, 语言本身和标准库都增加了很多新内容. 里面的某些特性, 会让你在代码编写时更优雅. 我的环境: 系统: ubuntu16.04 g++版本: g++5.4.0 使用c ...
- shared_ptr注意事项
对shared_ptr的Copy构造和Copy赋值,会改变引用计数,但是对shared_ptr中原始资源的Copy构造和Copy赋值,不会改变引用计数.因此存在下面的危险情况: 1.获取资源时,初始化 ...
- shared_ptr 的使用及注意事项
1. 声明#include <boost/shared_ptr.hpp> class UsersBitmap { ... } typedef boost::shared_ptr<Us ...
- shared_ptr 和 unique_ptr
c++11标准废除乐auto_ptr, C++ 标准库智能指针 使用这些智能指针作为将指针封装为纯旧 C++ 对象 (POCO) 的首选项. unique_ptr 只允许基础指针的一个所有者. 除非你 ...
- auto_ptr,shared_ptr 智能指针的使用
Q: 那个auto_ptr是什么东东啊?为什么没有auto_array?A: 哦,auto_ptr是一个很简单的资源封装类,是在<memory>头文件中定义的.它使用“资源分配即初始化”技 ...
- 智能指针auto_ptr & shared_ptr
转载:智能指针auto_ptr 很多人听说过标准auto_ptr智能指针机制,但并不是每个人都天天使用它.这真是个遗憾,因为auto_ptr优雅地解决了C++设计和编码中常见的问题,正确地使用它可以生 ...
- c/c++ 智能指针 shared_ptr 使用
智能指针 shared_ptr 使用 上一篇智能指针是啥玩意,介绍了什么是智能指针. 这一篇简单说说如何使用智能指针. 一,智能指针分3类:今天只唠唠shared_ptr shared_ptr uni ...
随机推荐
- 进程&线程 同步异步&阻塞非阻塞
2015-08-19 15:23:38 周三 线程 线程安全 如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码 线程安全问题都是由全局变量及静态变量引起的 若每个线程中对 ...
- SQL Server的“错误:9004”
客户的一台现场采集电脑崩溃,无法启动.用PE引导后,将MDF和LDF数据复制出来,往正常的数据库附加时,报如下错误: 处理数据库 'databasename' 的日志时出错.如果可能,请从备份还原.如 ...
- Java for LeetCode 237 Delete Node in a Linked List
Java实现如下: public class Solution { public void deleteNode(ListNode node) { if(node==null||node.next== ...
- 配置tomcat的虚拟路径
配置tomcat的虚拟路径有两个地方需要配置,以eclipse为例: ①在tomcat的server.xml中的host节点内添加 <Context path="/meims/user ...
- [javascript]获取系统时间函数
var oDate=new Date(); //初始化系统时间函数 alert(oDate.getHours()); //获取时 alert(oDate.getMinutes()); //获取分 al ...
- iOS计算字符串的宽度高度
OC开发中会遇到根据字符串和字体大小来算计算出字符串所占的宽高->> 封装方法如下: #import <Foundation/Foundation.h> #import < ...
- supersr--KVO/KVC
KVO内部实现原理 1.KVO是基于runtime机制实现的. 2.当某个类的对象第一次被观察是,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的set方法.派生类 ...
- September 30th 2016 Week 40th Friday
Elegance is the only beauty that never fades. 优雅是唯一不会褪色的美. Even the most beautiful apperance may los ...
- php常用判断的函数
empty($var) //用来检查变量是否为空(没有值或零值) isset($var) //这个//测试一个变量看它是否已被定义. gettype($var) ...
- Jmeter 提取http请求返回值里json数据参数化方法
第三方插件下载地址:http://jmeter-plugins.org/downloads/all/ 插件下载后解压:找到JMeterPlugins-Extras.jar,把JMeterPlugins ...