【C++】智能指针

本篇博文旨在介绍C++中的智能指针;从为什么引入它开始,分别实现了auto_ptr,scoped_ptr,unique_ptr,shared_ptr等智能指针;介绍了各个智能指针的特点;最后用防函数和智能指针实现了文件指针的管理


智能指针概念

智能指针是一个类,用它的对象管理着申请的内存空间,并通过作用域、生命周期来保证申请内存的释放,从而防止出现内存泄漏

内存泄漏举例

#include<iostream>
using namespace std;

//为什么需要智能指针
//每次返回都需要对指针进行处理
//很是麻烦
//所以要引出智能指针,可以自出进行内存的释放
void FunTest()
{
	int* p = new int[10];
	FILE* pFile = fopen("1.txt", "r");

	if (pFile == NULL)//如果打开失败,那么p就成为了一个野指针
	{
		return;
	}

	if (p != NULL)
	{
		delete[] p;
		p = NULL;
	}
}
在上述代码中,如果文件打开失败,那么p指针就没有进行释放,会造成内存泄漏

如果我们每次在返回语句前进行释放,当前面new申请的空间很多时,会不会太麻烦了

智能指针

1、RAII

RAII称为“资源获取就是初始化”,是c++等编程语言常用的管理资源

避免内存泄露的方法。它保证在任何情况下,

使用对象时先构造对象,最后析构对象。

2、boost库

了解智能指针就需要了解boost库,C++11引入的智能指针标准中,参考了boost库中对智能指针的实现

模拟实现智能指针

1、autoptr——不推荐使用的智能指针

template<typename T>
class AutoPtr
{
public:
	AutoPtr(T* ap = NULL)
		:_p(ap)
	{
		ap = NULL;
		cout << "AutoPtr()" << endl;
	}

	AutoPtr(AutoPtr& ap)
		:_p(ap._p)
	{
		ap._p = NULL;
		//ap = NULL;
	}

	AutoPtr& operator=(AutoPtr&ap)
	{
		if (this != &ap)
		{
			if (_p != NULL)
			{
				delete _p;
				_p = ap._p;
				ap._p = NULL;
			}
		}

		return *this;
	}

	~AutoPtr()
	{
		if (_p != NULL)
		{
			cout << "~AutoPtr()" << endl;
			delete[] _p;
			_p = NULL;
		}
	}

	T* Get()
	{
		return _p;
	}

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

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

private:
	T* _p;
};
auto_ptr的缺陷:

只能由一个对象来管理空间

当多个auto_ptr指向同一块空间时,会由于多次释放而导致崩溃

2、ownerptr——设置布尔变量来表示有没有管理空间

当进行拷贝构造,进行管理权限的交换

只有掌握空间权限的才可以进行释放

template <typename T>
class OwnerPtr
{
public:
	//构造函数
	OwnerPtr(T* ap = NULL)
		:_p(ap)
		, _owner(true)
	{
		cout << "OwnerPtr()" << endl;
	}

	//拷贝构造函数
	OwnerPtr(OwnerPtr<T>& ap)
		:_p(ap._p)
		, _owner(true)
	{
		ap._owner = false;
	}

	OwnerPtr<T>& operator=(OwnerPtr<T>& ap)
	{
		if (this != &ap)
		{
			if (_p != NULL)
			{
				delete _p;
				_p = ap._p;
				_owner = true;
				ap._owner = false;
			}
		}
		return *this;
	}

	~OwnerPtr()
	{
		if (_owner && _p != NULL)
		{
			cout << "~OwnerPtr()" << endl;
			delete _p;
			_p = NULL;
			_owner = false;//false代表释放完毕
		}
	}
	T* Get()
	{
		return _p;
	}
	T* operator*()
	{
		return *_p;
	}
	T* operator->()
	{
		return _p;
	}
private:
	T* _p;
	bool _owner;//表示对象是否占用资源
};

3、scopedptr——最容易实现的智能指针

scoped_ptr直接不允许进行拷贝以及赋值运算符重载,故而没有释放两次导致崩溃的事情发生

template<typename T>
class ScopedPtr
{
public:
	ScopedPtr(T* ap = NULL)
		:_p(ap)
	{}

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

	T* Get()
	{
		return _p;
	}

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

	T* operator->()
	{
		return _p;
	}
private:
	ScopedPtr(const ScopedPtr& ap);
	ScopedPtr<T>& operator=(const ScopedPtr& ap);
	//下面是另一种防止拷贝的方法
	/*ScopedPtr(const ScopedPtr& ap) = delete;
	ScopedPtr<T>& operator=(const ScopedPtr& ap) = delete;*/
protected:
	T* _p;
};

4、uniqueptr——类似于一个动态开辟的数组

由于可以用vector实现,所以用的不多

并且不存在于boost库

//unique_ptr
//重载了[]
//就比如一个vecotr开辟的动态数组
template<typename T>
class UniquePtr
{
public:
	UniquePtr(T* ap)
		:_p(ap)
	{}

	T& operator[](const size_t index)
	{
		return _p[index];
	}

	const T& operator[](const size_t index)const 
	{
		return _p[index];
	}

	T* Get()
	{
		return _p;
	}
protected:
	T* _p;
};

void Funtest()
{
	UniquePtr<int> p(new int[5]);
	p[0] = 1;
	p[1] = 2;
	p[2] = 3;
	p[3] = 4;
	p[4] = 5;
	for (size_t i = 0; i<5; i++)
	{
		cout << p[i] << " ";
	}
}

5、sharedptr——引用计数版本的只能指针

通过对管理一块空间的智能指针进行计数,只当_pCount == 0 的时候再进行释放

否则就 --_pCount

template<typename T>
class SharedPtr
{
public:
	SharedPtr(T* _ap)
		:_p(ap)
		, _pCount(NULL)
	{
		//当对象不为空时,对_pCount进行引用计数统计
		if (_p != NULL)
		{
			_pCount = new int[1];
		}
	}

	SharedPtr(SharedPtr<T> &ap)
		:_p(ap._p)
		, _pCount(ap._pCount)
	{
		//如果对象不为空
		//则增加引用计数
		if (p != NULL)
		{
			++(*_pCount);
		}
		sp._p = NULL;
	}

	SharedPtr<T> operator=(SharedPtr<T> &ap)
	{
		if (this != &ap)
		{
			//没有管理空间
			if (_pCount == NULL)
			{
				_p = ap._p;
				_pCount = ap._pCount;

				if (_pCount != NULL)
					++(*_pCount);
			}
			else if (*_pCount == 1)//独自管理一段空间
			{
				delete _p;
				delete _pCount;

				_p = ap._p;
				_pCount = ap._pCount;
				if (_pCount != NULL)
					++(*_pCount);
			}
			else//和别人共享空间
			{
				--(*_pCount);

				_p = ap._p;
				_pCount = ap._pCount;
				if (_pCount != NULL)
					++(*_pCount);
			}
		}
		return *this;
	}
protected:
	T* _p;
	int* _pCount;
};

6、sharedptr的循环引用解决方法---weakptr

sharedptr的循环引用

template<typename T>
class Node
{
public:
	Node(const T& value)
		: _value(value)
	{
		cout << "Node()" << endl;
	}
	
	~Node()
	{
		cout << "~Node()" << endl;
		cout << "this:" << this << endl;
	}

	shared_ptr<Node<T>> _pNext;
	shared_ptr<Node<T>> _pPre;
	T _value;
};

void TestSharedPtr()
{
	shared_ptr<Node<int>> s1(new Node<int>(1));
	shared_ptr<Node<int>> s2(new Node<int>(2));

	cout << "s1-use_count:" << s1.use_count() << endl;
	cout << "s2-use_count:" << s2.use_count() << endl;

	s1->_pNext = s2;
	s2->_pPre = s1;

	cout << "s1-use_count:" << s1.use_count() << endl;
	cout << "s2-use_count:" << s2.use_count() << endl;
}

我们看到,当 s1->_pNext = s2; s2->_pPre = s1; 时

他们各自的引用计数会自增

这样就会导致释放的时候  use_count -1 = 1;并不会释放

就造成了内存泄漏


利用weakptr解决循环引用问题

template<typename T>
class Node
{
public:
	Node(const T& value)
		: _value(value)
	{
		cout << "Node()" << endl;
	}
	
	~Node()
	{
		cout << "~Node()" << endl;
		cout << "this:" << this << endl;
	}

	weak_ptr<Node<T>> _pNext;
	weak_ptr<Node<T>> _pPre;
	T _value;
};

void TestSharedPtr()
{
	shared_ptr<Node<int>> s1(new Node<int>(1));
	shared_ptr<Node<int>> s2(new Node<int>(2));

	cout << "s1-use_count:" << s1.use_count() << endl;
	cout << "s2-use_count:" << s2.use_count() << endl;

	s1->_pNext = s2;
	s2->_pPre = s1;

	cout << "s1-use_count:" << s1.use_count() << endl;
	cout << "s2-use_count:" << s2.use_count() << endl;
}

实现文件指针释放的两种该方法

1、函数指针

//利用函数实现文件指针的关闭
void FClose(FILE* pf)
{
	if (pf)
		fclose(pf);
}

void FunTest2()
{
	FILE* p = fopen("1.txt", "r");
	shared_ptr<FILE> sp(p, FClose);
}

2、利用防函数定制删除器

//利用防函数来控制文件指针的关闭
class FClose//定制删除器
{
	void operator()(FILE* p)
	{
		if (p)
			fclose(p);
	}
};

void FunTest3()
{
	FILE* p = fopen("1.txt", "r");
	shared_ptr<FILE> sp(p, FClose);
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值