智能指针

一、智能指针

C++98最早提供的智能指针:
auto_ptr
Boost 提供的智能指针:
scope_ptr/scope_array
unique_ptr
shared_ptr
weak_ptr

下面,我分别针对上述提及指针一一说明。
1、auto_ptr: 自动管理由new动态分配的单个对象,其管理对象的生命周期结束时会调用delete自动释放。

  • 任意转移资源所有权,用户未知
//相关操作:
auto_ptr<int> ptr1(new int(5));
A ptr2(new int(10));//初始化对象ptr2

ptr1.get();		//返回底层对象地址
ptr1.reset(ptrr2);	//销毁指定的对象,并存储新的指针(指针重置)
ptr1.release();		//实现auto_ptr底层对象之间的初始化、赋值(释放当前指针的所有权)

2、scope_ptr: 在auto_ptr的基础上增加了一些限制范围。

  • 不能转换:其管理的对象的生命周期仅限于一个区间
  • 不能共享:拷贝、赋值均为private
  • 管理数组对象:用scope_array

3、unique_ptr: 这个只能指针可以完全代替auto_ptr, 甚至还对齐进行了扩展。

  • 任意转移资源所有权,用户可感知
  • 可以管理数组对象,即调用了delete[]
unique_ptr<int> ptr1 (new int(5));
unique_ptr<int []> ptr2(new int[3]{1, 2, 3});

4、shared_ptr: “最像指针的智能指针”。

  • 有管理对象的所有使用权
  • 能实现共享
  • 引用计数:通过引用计数来记录不同指针对同一对象的管理个数,同时决定该对象的释放时间,可以避免不引用计数的智能指针出现的问题,下面会详细说明。
    (引用计数为0时,说明此时已经没有指针对其进行管理了)

5、weak_ptr: “观察指针”、“悬浮的指针”,其出现完全针对于shared_ptr的辅助。

  • 指向由shared_ptr管理的对象,但不控制管理对象的生命周期,即不会改变引用计数值
  • operator-> 不允许通过weak_ptr访问对象
  • 观察指针:weak_ptr指针可以通过lock()操作提升为shared_ptr指针
  • 悬浮的指针:weak_ptr指针可以通过expired()操作检测出失效指针(指向对象引用计数为0,已销毁)
shared_ptr<int> sp(new int(5));	//此时指向对象引用计数为1
weak_ptr <int> wp(sp);		//wp也指向同一对象,但引用计数不加1

wp.expired();			//检测sp指向对象是否销毁

shared_ptr<int> p = wp.lock();	//将弱智能指针提升为强智能指针,此时指向对象引用计数加1
assert(p != NULL)		//lock()成功, p可以进行operator ->操作调用sp资源
				//lock()失败,资源已经释放

总结:

  • 智能指针均指向其对象的地址,故其使用方法同裸指针一样;
  • 底层操作实现均内联,因此并不比直接使用指针的代价更高;
  • 智能指针都是将new/delete或new[]/delete[]包装到堆上的动态对象中;
  • 将栈中内容出栈自动释放的特性移植到了堆上,实现了资源的自动释放;
  • 智能指针可以避免裸指针可能会出现的内存泄漏问题。

二、引用计数

显而易见,二者之间的差别就在于是否对资源进行引用计数,
上面提到的智能指针仅shared_ptr 与 weak_ptr 是引用计数的。
引用计数的原因:

  • 不带引用计数的指针—>浅拷贝问题。
    带引用计数的指针,它们在处理多个指针均指向同一内存资源这一情况时,会先原来的auto_ptr置空,只有最新的auto_ptr才能访问资源,而这些操作都是透明化的,这样若再次使用旧的auto_ptr便会产生浅拷贝问题
  • 带引用计数的指针,在遇到上述同一情况时,仅对该块内存的使用次数加一,多个指针均能对其进行访问操作,此时该内存资源为共享的,当且仅当引用计数为0,释放该内存资源,此时也不会出现野指针

三、实现MYshared_ptr

class MYshared_ptr
{
public:
	//增加一个引用计数
	void addRef(void *ptr)
	{
  		list<Node> iterator it = nodeList.begin();
  		for(; it != nodeList.end(); ++it)
  		{
  			if(ptr == it->_resptr)
  			{
  				it->_count++;
  				return;
  			}
 		}
 		nodeList.push_back(Node(ptr));
 	}
 	//减少一个引用计数
	void delRef(void *ptr)
	{
		list<Node> iterator it = nodeList.begin();
		for(; it != nodeList.end(); ++it)
		{		
   			if (it->_resptr == ptr)
   			{
   				if (it->_count == 1)
    				{
     					nodeList.erase(it);
    					 return 0;
    				}
    				else
    				{
    					it->_count--;
     					return it->_count;
    				}
			}
		}
		return -1;
	}
private:
	struct Node
 	{
  		Node(void *ptr = NULL):_resptr(ptr), _count(1){}
 		 void *_resptr;
  		int _count;
 	};			//某一资源信息
 	list<Node> nodeList;	//保存智能指针指向的所有资源
};

四、智能指针的循环/交叉引用问题

该问题出现在强智能指针的引用计数当中,结果会导致资源无法释放

class B;
class A
{
public:
	 A(){ cout << "A()" << endl; }
 	~A(){ cout << "~A()" << endl; }
 	shared_ptr<B> _ptrb;
};
class B
{
public:
 	B(){ cout << "B()" << endl; }
 	~B(){ cout << "~B()" << endl; }
 	shared_ptr<A> _ptra; 
};
int main(int argc, char* argv[])
{
 	shared_ptr<A> ptra(new A());
 	shared_ptr<B> ptrb(new B());
 	ptra->_ptrb = ptrb;
 	ptrb->_ptra = ptra;
}
//通过调试运行,得到结果为	
A()
B()
而A、B均为析构,调试发现程序结束前A、B的引用计数count均为1,这也就是造成没有析构的原因。

在这里插入图片描述

shared_ptr和weak_ptr的使用建议:
创建对象的地方使用强智能指针,其它地方一律使用弱智能指针

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值