C++智能指针

在C语言学习的时候,指针的功能虽然强大,但是也是十分危险,指针的释放和置空在C语言中十分重要,不正确的操作可能会导致内存泄露。那能否让程序自己去控制在不需要资源时自动将其归还给系统。

RAII  (Resource Acquisition IsInitialization) :

定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。这一技术常称为“资源分配即初始化”,简称 RAII。 

智能指针:  RAII思想的一种实现。

1.构造函数保存资源,析构函数释放资源。

2.想指针一样使用。

智能指针的提出和完成不是一蹴而就的,本文将通过在智能指针使用时遇到的问题,并将其逐步解决来一步步

解释说明C++中的指针的原理,在下文中将模拟实现C++提供的智能指针的部分代码,以便说明问题。

       因为智能指针一开始的 目的很简单:资源分配了,忘记释放,从而导致了内存泄露的问题。

auto-ptr:解决了基本的资源释放的问题,但是依然有缺陷,不建议使用。

缺陷:因为没有考虑引用计数,所以只有唯一的拥有者。即一个对象只能由一个auto_ptr管理,在给其他的

auto_ptr赋值(复制)、参数传递时,才会转移这种关系,auto_ptr有三种实现方式:

第一种实现:   管理权转移  ---  在赋值运算符的重载和拷贝构造函数中把原来的指针赋值个新的,再把原来的

指针置空。

这种方法存在两个隐患:

1.因为采用的方法是把上一个指针置为空,就导致并不符合智能指针的两个基本原则:想指针一样。

2.因为将上一个指针置空,就导致可能访问空指针。

template<class T>
class AutoPtr
{
public:
	AutoPtr(T* ptr)
		:_ptr(ptr)
	{
		cout << "Auto()" << endl;
	}
	
	AutoPtr(AutoPtr<T>& ap)                    //拷贝构造中将上一个指针置为空
		:_ptr(ap._ptr)
	{
		ap._ptr = NULL;
	}

	AutoPtr<T>& operator=(AutoPtr<T>& ap)         
	{
		if (_ptr != ap._ptr)
		{
			delete _ptr;

			_ptr = ap._ptr;
			ap._ptr = NULL;
		}
		return *this;
	}

	T& operator *()
	{
		return *_ptr;
	}
	
	T* operator->()
	{
		return _ptr;
	}

	~AutoPtr()
	{
		if (NULL != _ptr)
		{
			cout << "Auto delete: 0x%p" << _ptr << endl;
			delete _ptr;
			_ptr = NULL;
		}
	}

	T* GetPtr()
	{
		return _ptr;
	}

private:
	T* _ptr;
};

第二种实现: 由于置空可能会导致的问题,引入了标记owner,表示当前资源的实际拥有者。

缺点:在进行参数传递的时候,对生命周期短的对象赋值,因为owner的转移,在函数调用结束后,生命周期短的被析构,

当再次访问生命周期长的的时候,就会变成了野指针的访问

template<class T>
class AutoPtr
{
public:
	AutoPtr(T* ptr)
		:_ptr(ptr)
		, _owner(true)
	{
		cout << "Auto()" << endl;
	}

	AutoPtr(AutoPtr<T>& ap)
		:_ptr(ap._ptr)
	{
		ap._owner = false;
		_owner = true;
	}

	AutoPtr<T>& operator=(AutoPtr<T>& ap)
	{
		if (_ptr != ap._ptr)
		{
			delete _ptr;

			_ptr = ap._ptr;
			ap._owner = false;
			_owner = true;
		}
		return *this;
	}

	T& operator *()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

	~AutoPtr()
	{
		if (_ptr && _owner)
		{
			cout << "Auto delete: 0x%p" << _ptr << endl;
			delete _ptr;
			_ptr = NULL;
		}
	}

private:
	T* _ptr;
	bool _owner;					//置空可能会导致解引用出现错误,这里为了解决引入__owner
}; 

        第三种实现:在第一种实现的基础上运用了类型转换。(这里不进行实现)。 

        因为auto_ptr的种种弊病,在实际应用中几乎不会使用到auto_ptr。

scoped_ptr:粗暴地解决了因为指针的赋值导致不同的指针指向同一块内存,可能导致内存的重复释放。scoped_ptr阻止了指针的拷贝。

       可以方便的管理单个的堆内存对象。且独享所有权。

template <class T>
class ScopePtr
{
public:
	ScopePtr(T* ptr)
		:_ptr(ptr)
	{
	}

	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}
	
	~ScopePtr()
	{
		if (_ptr)
		{
			delete _ptr;
			cout << "~ScopePtr() --  " << _ptr <<endl;
			_ptr = NULL;
		}
	}
private:
	ScopePtr(const ScopePtr<T>& sp);					//防拷贝:只声明,不定义;声明成私有
	ScopePtr<T>& operator=(const ScopePtr<T>& sp);

protected:
	T* _ptr;
};


template <class T>
class ScopedArray
{
public:
	ScopedArray(T* ptr)
		:_ptr(ptr)
	{
	}

	T& operator[](size_t pos)
	{
		return _ptr[pos];
	}

	~ScopedArray()
	{
		if (_ptr)
		{
			delete _ptr;
			cout << "~ScopedArray() --  " << _ptr << endl;
			_ptr = NULL;
		}
	}
private:
	ScopedArray(const ScopePtr<T>& sp);					//防拷贝:只声明,不定义;声明成私有
	ScopedArray<T>& operator=(const ScopedArray<T>& sp);

protected:
	T* _ptr;
};

shared_ptr:允许拷贝和赋值,引入了引用计数,解决了指针建对象的管理权的问题。

缺陷:

1.需要定制删除器,即指针指向不同类型的变量,析构时的方式也不一样。

2.写出的代码不是线程安全的。

3.存在循环引用的问题。双向循环链表--- 前驱结点的next指针指向后继结点,后继结点的prev指针指向前驱结点。因为两个指针的释放都依赖于对方的释放,于是就造成了循环引用的问题。

template <class T>
class SharedPtr
{
	friend class WeakPtr<T>;
public:
	SharedPtr(T* ptr)
		:_ptr(ptr)
		, _Count(new int(1))
	{}

	SharedPtr(const SharedPtr<T>& sp)
		:_ptr(sp._ptr)
		, _Count(sp._Count)
	{
		++(*_Count);
	}
	SharedPtr<T>& operator=(const SharedPtr<T>& sp)
	{
		if (_ptr != sp._ptr)
		{
			if (--(*_Count) == 0)
			{
				delete _ptr;
				delete _Count;
			}
			_ptr = sp._ptr;
			_Count = sp._Count;
			(*sp._Count)++;
		}
		return *this;
	}

	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

	~SharedPtr()
	{
		if (--(*_Count) == 0)
		{
			delete _Count;
			if (_ptr)
			{
				delete _ptr;
				cout << "~SharedPtr() --  " << _ptr << endl;
				_ptr = NULL;
			}
		}
	}

protected:
	T* _ptr;
	int* _Count;
};

template <class T>
class SharedArray
{
public:
	SharedArray(T* ptr)
		:_ptr(ptr)
		, _Count(new int(1))
	{}

	SharedArray(const SharedArray<T>& sp)
		:_ptr(sp._ptr)
		, _Count(sp._Count)
	{
		++(*_Count);
	}
	SharedArray<T>& operator=(const SharedArray<T>& sp)
	{
		if (_ptr != sp._ptr)
		{
			if (--(*_Count) == 0)
			{
				delete _ptr;
				delete _Count;
			}
			_ptr = sp._ptr;
			_Count = sp._Count;
			(*sp._Count)++;
		}
		return *this;
	}

	T& operator[](size_t pos)
	{
		return _ptr[pos];
	}

	~SharedArray()
	{
		if (--(*_Count) == 0)
		{
			delete _Count;
			if (_ptr)
			{
				delete[] _ptr;
				cout << "~SharedArray() --  " << _ptr << endl;
				_ptr = NULL;
			}
		}
	}

protected:
	T* _ptr;
	int* _Count;
};

循环引用问题:

struct listnode
{
	sharedptr<listnode> _next;
	sharedptr<listnode> _prev;
	int _data;

	listnode()
		:_next(null)
		,_prev(null)
		,_data(0)
	{

	}
};

void testsharedptr()
{
	sharedptr<listnode> node1 = new listnode;
	sharedptr<listnode> node2 = new listnode;
	
	node1->_next = node2;
	node2->_prev = node1;
}


weak_ptr:  为了配合shared_ptr而引入的一种智能指针,为了解决shared_ptr的循环引用问题,它并不是用来管理空间资源的。

template<class T>
class WeakPtr;

template <class T>
class SharedPtr
{
	friend class WeakPtr<T>;
public:
	SharedPtr(T* ptr)
		:_ptr(ptr)
		, _Count(new int(1))
	{}

	SharedPtr(const SharedPtr<T>& sp)
		:_ptr(sp._ptr)
		, _Count(sp._Count)
	{
		++(*_Count);
	}
	SharedPtr<T>& operator=(const SharedPtr<T>& sp)
	{
		if (_ptr != sp._ptr)
		{
			if (--(*_Count) == 0)
			{
				delete _ptr;
				delete _Count;
			}
			_ptr = sp._ptr;
			_Count = sp._Count;
			(*sp._Count)++;
		}
		return *this;
	}

	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

	~SharedPtr()
	{
		if (--(*_Count) == 0)
		{
			delete _Count;
			if (_ptr)
			{
				delete _ptr;
				cout << "~SharedPtr() --  " << _ptr << endl;
				_ptr = NULL;
			}
		}
	}

protected:
	T* _ptr;
	int* _Count;
};
/解决循环引用  引入WeakPtr -- 

template<class T>
class WeakPtr
{
public:
	WeakPtr(const SharedPtr<T>& sp)			//不接受T*
		:_ptr(sp._ptr)
	{}

	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

private:
	T* _ptr;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值