智能指针的简单剖析和实现

在C语言中我们用指针来进行内存管理,这也是C语言的强大之处。然而,也正是指针的存在使得C语言变得令人懊恼,内存泄漏、垂悬指针等等问题。强大的C++则采用智能指针(Smart_Ptr)来处理这个问题.

好了,什么是智能指针呢?智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。这样以防止内存泄漏。

智能指针都有哪些种类呢?

通过上述表格可以看出有如此多的智能指针,C11标准库已经引进unique_ptr/shared_ptr/weak_ptr供我们使用。

下面来简单谈谈这些指针的原理和实现。

First――>auto ptr

auto ptr的实现原理主要是管理权的转移。它是所拥有的对象的唯一拥有者,也就是一个对象只有一个拥有者。

代码实现:

template <class T>
class AutoPtr
{
public:
	AutoPtr(T* Ptr)
		:_Ptr(Ptr)
	{}

	~AutoPtr()
	{
		if(_Ptr!=NULL)
		{
			delete _Ptr;
		}
	}

	AutoPtr(AutoPtr<T>& ap)
	{
		_Ptr=ap._Ptr;
		ap._Ptr=NULL;
	}

	AutoPtr<T> operator=(AutoPtr<T>& ap)
	{
		if(&ap!=this)
		{
			delete _Ptr;
			_Ptr=ap._Ptr;
			ap._Ptr=NULL;
		}
		return *this;
	}

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

	T* operator->()
	{
		return _Ptr;
	}
private:
	T* _Ptr;
};

通过代码我们可以看出由于auto ptr指针唯一性,即一个对象只能有一个auto ptr指针所指向它。因此,当auto ptr以传值方式被作为参数传递给某函数时,这时对象的原来拥有者就放弃了对象的拥有权,把它转移到被调用函数中的参数上,如果函数不再将拥有权传递出去,由于形参的作用域仅仅为函数内部,当函数退出时,它所指向的对象将被销毁。当函数返回一个auto ptr时,其拥有权又被转移到了调用端。因此,我们尽量不要使用auto ptr传参,或者引用传递。此外,auto ptr 还不能作为容器的成员,C++标准明确禁止这样做。

Second――>scoped ptr

scoped ptr与auto ptr类似,它实现的原理则是防拷贝,也就是它不能转移管理权,所以不能被赋值或者拷贝构造。那么,我们可以将拷贝构造和赋值运算符重载函数只声明不实现,并将其声明为保护,那么也就防止了别人在类外实现它。

代码实现:

template <class T>
class ScopedPtr
{
public:
	ScopedPtr(T* Ptr)
		:_Ptr(Ptr)
	{}

	~ScopedPtr()
	{
		if(_Ptr!=NULL)
		{
			delete _Ptr;
		}
	}

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

	T* operator->()
	{
		return _Ptr;
	}
protected:
	T* _Ptr;
	ScopedPtr(ScopedPtr<T>& sp);
	ScopedPtr<T>& operator=(ScopedPtr<T>& sp);
};

通过代码可以看出scoped ptr 的实现十分的简单粗暴,动态分配对象的生命周期限制在特定的作用域,采用scoped ptr可以有作用域保护,使用起来也优于auto ptr。

Third――>shared ptr

shared ptr顾名思义就是共享,所以也就是说多个指针可以指向同一个内存,它所采用的是引用计数的原理,也就是引进了一个计数器shared_count,用来表示当前有多少个智能指针对象共享指针指向的内存。因此shared_ptr可以做为STL容器的元素。析构函数中不是直接释放指针对应的内块,shared_count大于1则不释放内存只是将引用计数减1,只是计数等于1时释放内存。这样避免了一块内存被多次析构的问题。

代码实现:

template <class T>
class SharedPtr
{
public:
	SharedPtr(T* Ptr)
		:_Ptr(Ptr)
		,_Pcount(new long(1))
	{}

	~SharedPtr()
	{
		_Release();
	}

	SharedPtr(SharedPtr<T>& sp)
		:_Ptr(sp._Ptr)
		,_Pcount(sp._Pcount)
	{
		++(*_Pcount);
	}

	SharedPtr<T>& operator=(SharedPtr<T>& sp)
	{
		if(&sp!=this)
		{
			_Release();
		    _Ptr=sp._Ptr;
		    _Pcount=sp._Pcount;
		    ++(*_Pcount);
		}	
	}

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

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

	long UseCount()
	{
		return *_Pcount;
	}

	T* GetPtr()
	{
		return _Ptr;
	}

protected:
	T* _Ptr;
	long *_Pcount;
	void _Release()
	{
		if(--(*_Pcount)==0)
			{
				delete _Ptr;
				delete _Pcount;
			}
	}
};

由上述代码可知,我们在拷贝和赋值也会将引用计数进行递增,而实现也只是一般的复制。

动态对象的正确释放是编程中最容易出错的地方,利用智能指针可以更安全的使用动态对象,使得我们的程序更高效、安全。

你可能感兴趣的:(C++,智能指针,动态内存)