4.shared_ptr

shared_ptr简介

scoped_ptr虽然简单易用,但它不能共享所有权的特性却大大限制了其使用范围,而shared_ptr可以解决这一局限。顾名思义,shared_ptr是可以共享所有权的智能指针

//Form vs2010 header #include <memory>

//
// include auto_ptr shared_ptr weak_ptr unique_ptr in vs2010
//
//class _Ptr_base
	long use_count() const
		{	// return use count
		return (_Rep ? _Rep->_Use_count() : 0);
		}

//class shared_ptr
	explicit shared_ptr(_Ux *_Px)
		{	// construct shared_ptr object that owns _Px
		_Resetp(_Px);
		}
		
	_Myt& operator=(const _Myt& _Right)
		{	// assign shared ownership of resource owned by _Right
		shared_ptr(_Right).swap(*this);
		return (*this);
		}
		
	void reset()
		{	// release resource and convert to empty shared_ptr object
		shared_ptr().swap(*this);
		}
		
	void swap(_Myt& _Other)
		{	// swap pointers
		this->_Swap(_Other);
		}


	_Ty *get() const
		{	// return pointer to resource
		return (this->_Get());
		}
		
	_Ty *operator->() const
		{	// return pointer to resource
		return (this->_Get());
		}

	bool unique() const
		{	// return true if no other shared_ptr object owns this resource
		return (this->use_count() == 1);
		}
		
	//==	!=	<	<=	>	>=
	class _Ty2>
	bool operator==(const shared_ptr<_Ty1>& _S1,
		const shared_ptr<_Ty2>& _S2)
	{	// test if shared_ptr == shared_ptr
	return (_S1.get() == _S2.get());
	}

shared_ptr的特点

和前面介绍的scoped_ptr相比,shared_ptr可以共享对象的所有权,因此其使用范围基本上没有什么限制(还是有一些需要遵循的使用规则,下文中介绍),自然也可以使用在stl的容器中。另外它还是线程安全的,这点在多线程程序中也非常重要。

shared_ptr的使用规则

shared_ptr并不是绝对安全,下面几条规则能使我们更加安全的使用shared_ptr:
避免对shared_ptr所管理的对象的直接内存管理操作,以免造成该对象的重释放
shared_ptr并不能对循环引用的对象内存自动管理(这点是其它各种引用计数管理内存方式的通病)。
不要构造一个临时的shared_ptr作为函数的参数。
如下列代码则可能导致内存泄漏:
void test()
{
    foo(shared_ptr<implementation>(new    implementation()),g());
}
正确的用法为:
void test()
{
    shared_ptr<implementation> sp    (new implementation());
    foo(sp,g());//当函数g()抛出异常时,发生内存泄露
}

shared_ptr必须注意的一些地方

虽然shared_ptr是个非常好的东西,使用它可以使得c++程序不需要考虑内存释放的问题,但是还是有很多必须注意的地方。下面罗列了一些本人在实际工作中经常碰到的使用shared_ptr出问题的几种情况。 

1. shared_ptr多次引用同一数据

{
int* pInt = new int[100];
boost::shared_ptr<int> sp1(pInt);
// 一些其它代码之后…
boost::shared_ptr<int> sp2(pInt);
}
这种情况在实际中是很容易发生的,结果也是非常致命的,它会导致两次释放同一块内存,而破坏堆。 

2.使用shared_ptr包装this指针带来的问题

class tester 
{
public:
  tester()
  ~tester()
  {
    std::cout << "析构函数被调用!\n"; 
  }
public:
  boost::shared_ptr<tester> sget()
  {
    return boost::shared_ptr<tester>(this);
  }
};

int main()
{
  tester t;
  boost::shared_ptr<tester> sp =  t.sget(); // …
  return 0;
}
也将导致两次释放t对象破坏堆栈,一次是出栈时析构,一次就是shared_ptr析构。若有这种需要,可以使用下面代码。
//随同的助手 enable_shared_from_this很给力啊
class tester : public boost::enable_shared_from_this<tester>
{
public:
  tester()
  ~tester()
  {
  std::cout << "析构函数被调用!\n"; 
  }
public:
  boost::shared_ptr<tester> sget()
  {
  return shared_from_this();
  }
};

int main()
{
  boost::shared_ptr<tester> sp(new tester);
  // 正确使用sp 指针。
  sp->sget();
  return 0;
}

3. shared_ptr循环引用导致内存泄露

class parent;
class child; 

typedef boost::shared_ptr<parent> parent_ptr;
typedef boost::shared_ptr<child> child_ptr; 

class parent
{
public:
       ~parent() { 
              std::cout <<"父类析构函数被调用.\n"; 
       }
public:
       child_ptr children;
};

class child
{
public:
       ~child() { 
              std::cout <<"子类析构函数被调用.\n"; 
       }
public:
       parent_ptr parent;
};

int main()
{
  parent_ptr father(new parent());
  child_ptr son(new child);
  // 父子互相引用。
  father->children = son;
  son->parent = father;
  return 0;
}
如上代码,将在程序退出前,father的引用计数为2,son的计数也为2,退出时,shared_ptr所作操作就是简单的将计数减1,如果为0则释放,显然,这个情况下,引用计数不为0,于是造成father和son所指向的内存得不到释放,导致内存泄露。 

4. 在多线程程序中使用shared_ptr应注意的问题

class tester 
{
public:
  tester() {}
  ~tester() {}
  // 更多的函数定义…
};

void fun(boost::shared_ptr<tester> sp)
{
  // !!!在这大量使用sp指针.
  boost::shared_ptr<tester> tmp = sp;
}

int main()
{
  boost::shared_ptr<tester> sp1(new tester);
  // 开启两个线程,并将智能指针传入使用。
  boost::thread t1(boost::bind(&fun, sp1));
  boost::thread t2(boost::bind(&fun, sp1));
  t1.join();
  t2.join();
  return 0;
}
这个代码带来的问题很显然,由于多线程同是访问智能指针,并将其赋值到其它同类智能指针时,很可能发生两个线程同时在操作引用计数(但并不一定绝对发生),而导致计数失败或无效等情况,从而导致程序崩溃,如若不知根源,就无法查找这个bug,那就只能向上帝祈祷程序能正常运行。
可能一般情况下并不会写出上面这样的代码,但是下面这种代码与上面的代码同样,如下:
class tester 
{
public:
  tester() {}
  ~tester() {}
public:
  boost::shared_ptr<int> m_spData; // 可能其它类型。
};

tester gObject;

void fun(void)
{
  // !!!在这大量使用sp指针.
  boost::shared_ptr<int> tmp = gObject.m_spData;
}

int main()
{
  // 多线程。
  boost::thread t1(&fun);
  boost::thread t2(&fun);
  t1.join();
  t2.join();
  return 0;
}
情况是一样的。要解决这类问题的办法也很简单,使用boost.weak_ptr就可以很方便解决这个问题。第一种情况修改代码如下:
class tester 
{
public:
  tester() {}
  ~tester() {}
  // 更多的函数定义…
};

void fun(boost::weak_ptr<tester> wp)
{
  boost::shared_ptr<tester> sp = wp.lock;
  if (sp)
  {
    // 在这里可以安全的使用sp指针.
  }
  else
  {
    std::cout << “指针已被释放!” << std::endl;
  }
} 

int main()
{
  boost::shared_ptr<tester> sp1(new tester);
  boost.weak_ptr<tester> wp(sp1);
  // 开启两个线程,并将智能指针传入使用。
  boost::thread t1(boost::bind(&fun, wp));
  boost::thread t2(boost::bind(&fun, wp));
  t1.join();
  t2.join();
  return 0;
}
boost.weak_ptr指针功能一点都不weak,weak_ptr是一种可构造、可赋值以不增加引用计数来管理shared_ptr的指针,它可以方便的转回到shared_ptr指针,使用weak_ptr.lock函数就可以得到一个shared_ptr的指针,如果该指针已经被其它地方释放,它则返回一个空的shared_ptr,也可以使用weak_ptr.expired()来判断一个指针是否被释放。

boost.weak_ptr不仅可以解决多线程访问带来的安全问题,而且还可以解决上面第三个问题循环引用。Children类代码修改如下,即可打破循环引用:
class child
{
public:
  ~child() { 
   std::cout <<"子类析构函数被调用.\n"; 
  }
public:
  boost::weak_ptr<parent> parent;
};
因为boost::weak_ptr不增加引用计数,所以可以在退出函数域时,正确的析构。

shared_ptr一个小例子

#ifndef	__SHARED_PTR_TEST_H__ 
#define __SHARED_PTR_TEST_H__

class Test
{
public:
	Test()
	{

	}
	~Test()
	{

	}
};

#endif	//__SHARED_PTR_TEST_H__
#include "shared_ptrTest.h"
#include <memory>
#include <iostream>
using namespace std;

int main()
{
	shared_ptr<Test> t1(new Test());
	cout<<t1.use_count()<<endl;
	shared_ptr<Test> t2=t1;
	shared_ptr<Test> t3(t2);
	if (t1==t2)
	{
		cout<<"t1==t2"<<endl;
	}
	cout<<t2.use_count()<<endl;
	t1.reset();
	cout<<t2.use_count()<<endl;
	t2.reset();
	cout<<t3.use_count()<<endl;
	//delete t1 & t2, so t3 only one ref
	if (t3.unique())
	{
		cout<<"t3.unique() is true!"<<endl;
	}
	t3.reset();
	cout<<t3.use_count()<<endl;

	char ch;
	cin>>ch;
	return 0;
}

from:
http://www.cnblogs.com/TianFang/archive/2008/09/19/1294521.html
http://blog.sina.com.cn/s/blog_508938e10100f12x.html
http://www.rosoo.net/a/201012/10555.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值