C++之shared_ptr总结

Share_ptr也是一种智能指针。类比于auto_ptr学习。所以推荐先学习auto_ptr,再来学习shared_ptr。本博客的前两个就是auto_ptr的总结。希望感兴趣的朋友可以看看。

Shared_ptr和auto_ptr最大的区别就是,shared_ptr解决了指针间共享对象所有权的问题,也就是auto_ptr中的赋值的奇怪问题。所以满足了容器的要求,可以用于容器中。而auto_ptr显然禁止共享对象所有权,不可以用于容器中。

[cpp] view plain copy

int * a=new int(2);  
shared_ptr<int> sp1(a);  
shared_ptr<int> sp2(sp1);     OK  

当然shared_ptr作为一种智能指针,也拥有和shared_ptr一些相似的性质。它们本质上都是类,但是使用起来像指针。它们都是为了解决防止内存泄漏的解决方案。都是运用了RAII技术来实现的。

注意:使用shared_ptr也要引用头文件#include

由于shared_ptr的源码过于复杂,我们不给出源码。类比于auto_ptr学习.

  1. 首先类shared_ptr有两个成员变量。T * px和unsign long * pn;

T * px;显然和auto_ptr一样,用于储存对象的指针。

pn用于记录有多少个shared_ptr拥有同一个对象。pn是shared_ptr对象间共享的,类似于static成员变量。

[cpp] view plain copy

template<class T>  
class shared_ptr{  
private:  
       T *px; // contained pointer  
    unsignedlong* pn; // reference counter  
}  

总结:其实shared_ptr的原理,就是使用px来记录指针,使用pn来记录px指向的对象的拥有者share_ptr的个数,当一个shared_ptr对象达到作用域时,不会释放资源,只有当pn变为0的时候,才会释放指针指向的资源。

  1. 一个简单实现的源码(仍然看懂源码还是最重要的。)

[cpp] view plain copy

#pragma once  
//shared_ptr的简单实现版本  
//基于引用记数的智能指针  
//它可以和stl容器完美的配合  
namespace boost  
{  
template<class T>  
class shared_ptr  
{  
typedef unsigned longsize_type;  
private:  
       T *px; // contained pointer  
   size_type* pn; // reference counter  
public:  
//构造函数---------------------------------------------------2  
/* 
int* a=new int(2); 
shared_ptr<int> sp; 
shared_ptr<int> sp(a); 
*/  
explicitshared_ptr(T* p=0) : px(p)  
{  
   pn = new size_type(1);  
}  
   
/* 
Derived d; 
shared_ptr<Base> ap(d); 
*/  
template<typename Y>  
shared_ptr(Y* py)  
{  
pn = newsize_type(1);  
px=py;  
}  
//copy构造函数------------------------------------------------  
/* 
int * a=new int; 
shared_ptr<int> sp(a); 
shared_ptr<int> sp1(sp); 
*/  
shared_ptr(constshared_ptr& r) throw(): px(r.px)  
{  
++*r.pn;  
pn = r.pn;  
}  
   
/* 
shared_ptr<Derived>sp1(derived); 
shared_ptr<Base> sp2(sp1); 
*/  
template<typename Y>  
shared_ptr(constshared_ptr<Y>& r)//用于多态  
{  
px = r.px;  
++*r.pn;  
pn = r.pn; //shared_count::op= doesn't throw  
}  
//重载赋值operator=--------------------------------------------  
shared_ptr& operator=(const shared_ptr& r) throw()  
{  
if(this== &r) return *this;  
dispose();  
px = r.px;  
++*r.pn;  
pn = r.pn;  
return *this;  
}  
template<typename Y>  
shared_ptr& operator=(const shared_ptr<Y>& r)//用于多态  
{  
dispose();  
px = r.px;  
++*r.pn;  
pn = r.pn; //shared_count::op= doesn't throw  
return *this;  
}  
   
~shared_ptr() { dispose(); }  
void reset(T* p=0)  
{  
if ( px == p ) return;  
if (--*pn == 0)  
{ delete(px); }  
else  
{ // allocate newreference  
// counter  
// fix: prevent leak if new throws  
try { pn = new size_type; }  
catch (...) {  
// undo effect of —*pn above to  
// meet effects guarantee  
++*pn;  
delete(p);  
throw;  
} // catch  
} // allocate newreference counter  
*pn = 1;  
px = p;  
} // reset  
reference operator*()const throw(){ return *px; }  
pointer operator->()const throw(){ return px; }  
pointer get() constthrow(){ returnpx; }  
size_type use_count() constthrow()//  
{ return *pn; }  
bool unique() const throw()//  
{ return *pn ==1; }  
private:  
void dispose() throw()  
{  
if (--*pn == 0)  
{ delete px; delete pn; }  
}  
}; // shared_ptr  
template<typename A,typenameB>  
inline bool operator==(shared_ptr<A>const & l, shared_ptr<B> const & r)  
{  
return l.get() == r.get();  
}  
template<typename A,typenameB>  
inline bool operator!=(shared_ptr<A>const & l, shared_ptr<B> const & r)  
{  
return l.get() != r.get();  
}  
}//namespace boost  

要注意的地方:

  1. Shared_ptr和auto_ptr都有类似的规定:

看看它们的copy构造和重载赋值都可以看出:

不允许

[cpp] view plain copy

int* a=new int(2);  
shared_ptr<int>sp=a;//  error  
sp=a;//    error  

就是不允许使用一个纯指针给一个智能指针赋值或copy构造。只能使用智能指针给另一个智能指针赋值或copy构造。

[cpp] view plain copy

   int* a=new int(2);  
hared_ptr<int> sp(a);//构造函数  
shared_ptr<int> sp1(sp);//copy构造  
   sp1=sp;//赋值  

在auto_ptr中也是相同的。

  1. 注意shared_ptr的几个函数

Ø Reset()函数:重置函数

标准中的是:

[cpp] view plain copy

   int* a=new int(2);  
    int* b=new int(3);  
 shared_ptr<int> sp2(a);  
 shared_ptr<int> sp1(a);  
shared_ptr<int> sp(a);  
    sp.reset(b);  
   sp.reset();  
   sp.reset(sp2);  -----!!!也是可以的。  

使得sp获得b的拥有权。失去a的拥有权。注意这会使得a的拥有者少1.当a的拥有者变为0时,就会释放a的资源。

Ø Swap()函数:交换函数

[cpp] view plain copy

int* a=new int(2);  
shared_ptr<int> sp(a);  
shared_ptr<int> sp1(a);  
sp.swap(sp1);  

就是两个shared_ptr中的px和pn都互换一下。

Ø Get()函数:返回px

Ø Use_count函数:返回*pn,就是对象的拥有者的数量。

Ø Unique函数:令*pn=1;让对象的拥有者的数量变为1。返回bool

Ø 同时share_ptr也重载了*和->

  1. tr1中重载了几个有关shared_ptr的符号:

template<classT, class U>

booloperator==(shared_ptr const& a, shared_ptr const& b);

转自 http://blog.csdn.net/u013696062/article/details/39665247

判断拥有的对象是否是一样的

template<classT, class U>

bool operator!=(shared_ptr const&a, shared_ptr const& b);

判断拥有的对象是否是不一样的

template<classT, class U>

bool operator<(shared_ptrconst& a, shared_ptr const& b);

重载了小于号,在STL中的LIST中非常有用。

[cpp] view plain copy

int* a=new int(2);  
int* b=new int(3);  
shared_ptr<int> sp(a);  
shared_ptr<int> sp1(b);  
if(sp<sp1)  
       cout<<"2222"<<endl;  
  1. 注意真实中shared_ptr中没有public dispose这个函数,这里只是为了避免代码重复。

  2. 注意shared_ptr中的析构函数中不是直接释放资源,而是调用了dispose函数,如果*pn==0了,才会释放资源。

8.shared_ptr的多线程的安全性

shared_ptr 本身不是 100%线程安全的。它的引用计数本身是安全且无锁的,但对象的读写则不是,因为shared_ptr有两个数据成员,读写操作不能原子化。根据文档,shared_ptr的线程安全级别和内建类型、标准库容器、string一样,即:

一个 shared_ptr 实体可被多个线程同时读取;
两个的 shared_ptr 实体可以被两个线程同时写入,“析构”算写操作;
如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁。

发现了两个非常有意思的东西:

  1. 看tr1中的源码中发现两个这样的东西:

template<class Y, classD> shared_ptr(Y * p, D d);

template<class Y, classD> void reset(Y * p, D d);

其中的D d是个什么东西?源码的解释是d是一个deleter(删除器)。至此我们突然发现我们可以给shared_ptr指定一个删除器,当*pn==0的时候,不去释放资源,而去调用我们自己给它的删除器。

当shared_ptr的引用次数为0的时候,share_ptr就会调用释放函数来释放资源。

当我们希望引用次数为0的时候,shared_ptr不释放资源,而是调用我们指定的操作的时候,就会用到D d;

[cpp] view plain copy

void foo(int * d)  
{  
       cout<<"1234"<<endl;  
}  
   
int _tmain(int argc, _TCHAR* argv[])  
{  
       int* a=new int(2);  
       shared_ptr<int> sp(a,foo);  
       shared_ptr<int> sp1(sp);  
       sp.reset();  
       sp1.reset();  
       //_CrtDumpMemoryLeaks();  
       system("pause");  
       return 0;  
}  

注意!:

  1. 指定的删除器的参数必须是int*;和shared_ptr中的int对应。不能是其他的,或者为空也是错的。因为系统会把shared_ptr的对象px赋给删除器的参数,我们也可以在删除器中释放资源。

  2. 只有a的引用次数为0才会调用,所以如果没有sp1.reset()。也不会调用foo函数。

  3. 使用shared_ptr的时候,要小心,想一想操作的内在含义才去做。

1>

[cpp] view plain copy

int* a=new int(2);  
shared_ptr<int> sp(a);  
shared_ptr<int> sp1(sp);  
sp.reset();//--------(1)  
sp.reset();//--------(2)  

这里(1)是重置了sp,注意(2)是没有任何作用的,不能使得a的引用次数变为0.想一想reset的函数内部,(2)的时候,sp中的对象pn已经为空了,则不能改变*pn的值了。

2>

[cpp] view plain copy

int* a=new int(2);  
  shared_ptr<int> sp(a);//----------(1)  
  shared_ptr<int> sp1(a);//---------(2)  

注意:这里的(2)也是不对的。想一想shared_ptr的构造函数,(1)的时候,sp的px指向a,且pn为1.而(2)的时候,px指向a,且pn也是1.这显然就问题了。a被引用了2次,但是*pn为1.在最后作用域达到的时候,就会释放2次内存,这就会引发异常。

总结:shared_ptr和auto_ptr的区别。

Shared_ptr有两个变量,一个记录对象地址,一个记录引用次数

Auto_ptr只有一个变量,用来记录对象地址

Shared_ptr可用多个shared_ptr拥有一个资源。

Auto_ptr只能一个auto_ptr拥有一个资源

Shared_ptr可以实现赋值的正常操作,使得两个地址指向同一资源

Auto_ptr的赋值很奇怪,源失去资源拥有权,目标获取资源拥有权

Shared_ptr到达作用域时,不一定会释放资源。

Auto_ptr到达作用于时,一定会释放资源。

Shared_ptr存在多线程的安全性问题,而auto_ptr没有。

Shared_ptr可用于容器中,而auto_ptr一般不可以用于容器中。

Shared_ptr可以在构造函数、reset函数的时候允许指定删除器。而auto_ptr不能。

还有这里说一句:使用智能指针(不管shared_ptr还是auto_ptr),都要清除源码内部的实现原理,使用起来才不会错。而且使用的时候,一定要想一想函数内部的实现原理再去使用。切记小心。

shared_ptrC++中用于动态内存管理的智能指针之一。它能够记录对象被引用的次数,主要用于管理动态创建的对象的销毁。使用shared_ptr可以避免内存泄漏和悬挂指针等问题。 在C++中,实现shared_ptr通常需要以下几个步骤: 1. 定义一个模板类,例如SHARED_ptr,该类将作为智能指针使用。 2. 在SHARED_ptr类中,定义一个指针成员变量ptr,用于指向实际的对象。 3. 在SHARED_ptr类中,定义一个计数器类的指针成员变量refcount,用于记录对象被引用的次数。 4. 在SHARED_ptr类中,实现构造函数,用于初始化指针和计数器。 5. 在SHARED_ptr类中,实现拷贝构造函数和赋值操作符重载,用于处理多个智能指针共享同一对象的情况。 6. 在SHARED_ptr类中,实现析构函数,用于释放对象的内存空间。 7. 在SHARED_ptr类中,实现箭头运算符重载和解引用运算符重载,用于访问对象的成员函数和数据。 8. 在SHARED_ptr类外部,实现计数器类RefCount,用于记录对象被引用的次数,并在引用次数为0时释放对象的内存空间。 实现shared_ptr的详细代码如下所示: ```cpp template <class T> class SHARED_ptr { private: T* ptr; // 用来指向堆区对象 RefCount<T>* refcount; // 指向计数器对象的指针 public: SHARED_ptr(T* p) : ptr(p), refcount(new RefCount<T>()) { refcount->increment(); } SHARED_ptr(const SHARED_ptr<T>& other) : ptr(other.ptr), refcount(other.refcount) { refcount->increment(); } ~SHARED_ptr() { if (refcount->decrement() == 0) { delete ptr; delete refcount; } } T* operator->() const { return ptr; } T& operator*() const { return *ptr; } SHARED_ptr& operator=(const SHARED_ptr<T>& other) { if (this != &other) { if (refcount->decrement() == 0) { delete ptr; delete refcount; } ptr = other.ptr; refcount = other.refcount; refcount->increment(); } return *this; } }; ``` 以上是一个简单的C++实现shared_ptr的示例代码。通过使用shared_ptr,我们可以方便地管理动态创建的对象的生命周期,并避免内存泄漏和悬挂指针等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值