智能指针

RAII

利用对象生命周期控制程序资源的简单技术,在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期始终保持有效,在析构时释放资源,就是把管理资源的责任托管给了对象

优点

  • 不需要显式释放资源
  • 对象的资源在生命周期内始终有效

智能指针的原理

  • RAII特性
  • 重载operator*和operator->,具有像指针一样的行为

auto_ptr

template < class T>
class Auto_Ptr{
public:
	Auto_Ptr(T* ptr = nullptr)
		:_ptr(ptr)
	{}
	~Auto_Ptr(){
		if (_ptr)
			delete _ptr;
	}
	//发生拷贝时将ap与资源断开联系,防止一块空间被多个对象使用
	//但是拷贝后的ap指针对象赋空,后续访问ap对象会出问题
	Auto_Ptr(Auto_Ptr<T>& ap)
		:_ptr(ap._ptr)
	{
		ap._ptr = nullptr;
	}
	Auto_Ptr<T>& operator= (Auto_Ptr<T>& ap){
		if (this != &ap){
			if (_ptr)//释放当前对象资源
				delete _ptr;
			//转移ap的资源
			_ptr = ap._ptr;
			ap._ptr = nullptr;
		}
		return *this;
	}
	T& operator*(){
		return *_ptr;
	}
	T* operator->(){
		return _ptr;
	}
private:
	T* _ptr;
};

unique_ptr

auto_ptr拷贝构造后原对象赋空,所以C++11提供了unique_ptr,实现原理就是防拷贝

template <class T>
class Unique_Ptr{
public:
	Unique_Ptr(T* ptr = nullptr)
		:_ptr(ptr)
	{}
	~Unique_Ptr(){
		if (_ptr)
			delete _ptr;
	}
	T& operator*(){
		return *_ptr;
	}
	T* operator->(){
		return _ptr;
	}
private:
	Unique_Ptr(Unique_Ptr<T> const&);
	Unique_Ptr& operator=(Unique_Ptr<T> const&);
	//C++11写法
	//Unique_Ptr(Unique_Ptr<T> const&) = delete;
	//Unique_Ptr& operator=(Unique_Ptr<T> const&) = delete;
	T* _ptr;
};

shared_ptr

  • C++11提供了支持拷贝的shared_ptr,实现原理是通过引用计数的方式实现多个shared_ptr对象之间共享资源
  • shared_ptr内部维护了一份计数,用来记录该份资源被几个对象共享
  • 在对象调用析构函数时,计数减一,当计数为0时,说明自己是最后一个资源,必须释放该资源,如果不是0,说明有其他对象还在使用该资源,不能释放
template <class T>
class Shared_Ptr{
public:
	Shared_Ptr(T* ptr = nullptr)
		:_ptr(ptr)
		, _pRefCount(new int(1))
		, _pMutex(new mutex)
	{
		//如果是空指针对象,引用计数给0
		if (_ptr == nullptr)
			*_pRefCount = 0;
	}
	~Shared_Ptr(){
		Release();
	}
	Shared_Ptr(Shared_Ptr<T> const& sp)
		:_ptr(sp._ptr)
		, _pRefCount(sp._pRefCount)
		, _pMutex(sp._pMutex)
	{
		//拷贝一非空指针对象增加引用计数
		if (_ptr)
			AddRefCount();
	}
	Shared_Ptr<T>& operator=(const Shared_Ptr<T>& sp){
		if (_ptr != sp._ptr){
			Release();
			_ptr = sp._ptr;
			_pRefCount = sp._pRefCount;
			_pMutex = sp._pMutex;
			if (_ptr)
				AddRefCount();
		}
		return *this;
	}
	T& operator*(){
		return *_ptr;
	}
	T* operator->(){
		return _ptr;
	}
	int AddRefCount(){
		//引用计数加一的原子操作
		_pMutex->lock();
		++(*_pRefCount);
		_pMutex->unlock();
		return *_pRefCount;
	}
	int SubRefCount(){
		//引用计数减一的原子操作
		_pMutex->lock();
		--(*_pRefCount);
		_pMutex->unlock();
		return *_pRefCount;
	}
private:
	void Release(){
		if (_ptr&&SubRefCount() == 0){
			//引用计数为0时释放资源
			delete _ptr;
			delete _pRefCount;
		}
	}
	T* _ptr;
	int* _pRefCount;//引用计数
	mutex* _pMutex;//互斥锁
};

线程安全

  • 智能指针的引用计数时线程安全的
  • 智能指针管理的对象存放在堆上,两个线程同时去访问会导致线程安全问题
shared_ptr<int> X(new int);//线程间共享的shared_ptr
shared_ptr<int> A;         //线程A的局部变量
shared_ptr<int> B(new int);//线程B的局部变量

创建好线程

先执行A = X,指针对象赋值过去,但引用计数还没来得及赋值就切换到了另一个线程

执行X = B,先是指针赋值过去

再把引用计数赋值过去

此时int1对象已销毁,x._ptr成了空悬指针,最后回到线程A

所以多线程读写shared_ptr时需要加锁

循环引用

struct ListNode{
	int _data;
	//shared_ptr<ListNode> _prev;
	//shared_ptr<ListNode> _next;
	weak_ptr<ListNode> _prev;
	weak_ptr<ListNode> _next;
	~ListNode(){
		cout << "~ListNode" << endl;
	}
};

分析

  • 当node1的_next指向node2,node2的_prev指向node1时
  • 当node1和node2析构引用计数减到1,但是_prev和_next还指向节点
  • 当_prev和_next析构,node1和node2也就释放了
  • 但是node1和node2分别由_prev和_next管理,_prev和_next属于node的成员,谁也不会释放

weak_ptr

  • 为解决shared_ptr的循环引用的问题C++11标准库引入入weak_ptr
  • weak_ptr不是智能指针,只能配合shared_ptr使用
  • weak_ptr不会改变循环技术,析构函数不释放资源

删除器

如果对象时静态建立的,shared_ptr设计了一个仿函数删除器来解决

template <class T>
struct FreeFunc{
    void operator()(T* ptr){
        free(ptr);
    }
};
template <class T>
struct DeleteFunc(){
    void operator()(T* ptr){
        delete[] ptr;
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值