智能指针

本文深入探讨了智能指针的概念及其实现方式,包括解决内存泄漏问题的多种智能指针类型如Auto_ptr、Scoped_ptr、Scoped_array、Shared_ptr、Shared_array等,并详细分析了它们的工作原理及其优缺点。
摘要由CSDN通过智能技术生成

一.为什么会有智能指针

智能指针其实是解决不经意间内存泄露的一个机制~

首先看下面的代码:

bool connectserver()
{
	cout << "链接服务" << endl;
	return rand() % 5 == 0;
}
void pushmail()
{
	cout << "链接成功" << endl;
	cout << "发送邮件" << endl;
	if (rand() % 2 == 0)
	{
		throw 1;
	}
}
void fun()
{
	char *p= new char[1024 * 1024];
	if (!connectserver())
	{
		cout << "链接数据库失败" << endl;
		delete[]p;
		return;
	}
	pushmail();
	delete[]p;
}
int main()
{
	while (1)
	{
		try
		{
			fun();
		}
		catch (int e)
		{
			cout << "抛出异常" << endl;
		}
			Sleep(1000);
	}
	system("pause");
	return 0;
}


对于上述代码,执行过程乱跳,很容易造成内存泄露;


二.怎么解决呢?

首先要知道RAII:


如下解决代码

template<typename T>
class Auto_ptr
{
private:
	T *_ptr;
public:
	Auto_ptr(T *ptr)
		:_ptr(ptr)//初始化获取资源
	{}
	~Auto_ptr()
	{
		delete[]_ptr;
		_ptr = NULL;
	}
};
bool connectserver()
{
	cout << "链接服务" << endl;
	return rand() % 5 == 0;
}
void pushmail()
{
	cout << "链接成功" << endl;
	cout << "发送邮件" << endl;
}
void fun()
{
	Auto_ptr<char> ap1 = new char[1024 * 1024];
	if (!connectserver())
	{
		cout << "链接数据库失败" << endl;
		return;
	}
	pushmail();
}
int main()
{
	while (1)
	{
			fun();
			Sleep(1000);
	}
	system("pause");
	return 0;
}

看如下代码

template<typename T>
class Auto_ptr
{
private:
	T *_ptr;
public:
	Auto_ptr(T *ptr)
		:_ptr(ptr)//初始化获取资源
	{}
	~Auto_ptr()
	{
		delete[]_ptr;
		_ptr = NULL;
	}
};
这个存在的问题,既然是智能指针就需要有指针的功能,需要operator*和operator->

重载后的代码

template<typename T>
class Auto_ptr
{
private:
	T *_ptr;
public:
	Auto_ptr(T *ptr)//RAII不等于智能指针,智能指针只是RAII的一种思想的实现
		:_ptr(ptr)//初始化获取资源
	{}
	~Auto_ptr()
	{
		delete[]_ptr;
		_ptr = NULL;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
};

除了重载那两个符号的问题还有什么问题?

void test()
{
	int *p = new int(10);
	Auto_ptr<int> ap1(p);
	Auto_ptr<int> ap2(ap1);
}


程序崩溃,因为其指向一块内存,注意和string的区别,智能指针只是帮忙管理,不进行操作的

通过的初始化崩溃问题引出

c98之前人们解决的方案是-----》权限管理,但是其也是带有严重的缺陷的解决问题

接下来给出权限转移的代码,这就需要我们自己来写拷贝构造函数了,代码如下

template<typename T>
class Auto_ptr
{
private:
	T *_ptr;
public:
	Auto_ptr(T *ptr)//RAII不等于智能指针,智能指针只是RAII的一种思想的实现
		:_ptr(ptr)//初始化获取资源
	{}
	~Auto_ptr()
	{
		delete[]_ptr;
		_ptr = NULL;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T operator->()
	{
		return _ptr;
	}
	Auto_ptr(Auto_ptr<T>& p)//拷贝构造函数,拷贝完就释放原来的空间~
		:_ptr(p._ptr)
	{
		p._ptr = NULL;
	}
};

除此之外还有赋值运算符重载函数,不然赋值的时候还会崩溃

代码如下

template<typename T>
class Auto_ptr
{
private:
	T *_ptr;
public:
	Auto_ptr(T *ptr)//RAII不等于智能指针,智能指针只是RAII的一种思想的实现
		:_ptr(ptr)//初始化获取资源
	{}
	~Auto_ptr()
	{
		delete[]_ptr;
		_ptr = NULL;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T operator->()
	{
		return _ptr;
	}
	Auto_ptr(Auto_ptr<T>& p)//拷贝构造函数,拷贝完就释放原来的空间~
		:_ptr(p._ptr)
	{
		p._ptr = NULL;
	}
	   Auto_ptr<T>& operator=(Auto_ptr<T>& ap)
	{
			if (this != &ap)
			{
				if (_ptr)//先释放ap2空间
				{
					delete _ptr;
				}
				_ptr = ap._ptr;
				ap._ptr = NULL;
			}
			return *this;
	  }
};
void test()
{
	int *p = new int(10);
	Auto_ptr<int> ap1(p);
	Auto_ptr<int> ap2(ap1);
	ap2 = ap1;
}

这就是autoptr,尽管如此解决了初始化和赋值的问题,但是比如ap2=ap1,ap1这块空间就不能使用了,还是有严重缺陷的,但是为什么还要知道他呢,因为他还在我们的库里面,C++一旦产生的语法就不能取消,这就是向前兼容

上面的不好用怎么办呢?因为实在太烂了,所以有人就会造轮子,就出现了boost,他不是C++的标准库但是是比较重要的一个库
他产生了自己一套智指针

其实C++还有自己的几种智能指针

1.unique_ptr

2.shared_ptr

weak_ptr他其实是参考了boost

对上图中的代码做一些解释

scoped简单粗暴,防拷贝的设计

shared引用计数功能更全比较复杂,效率相对会比较低,还有循环引用的缺陷,用weak ptr去解决
1.首先来说名scoped ptr

template<class T>
class ScopdePtr
{
public:
	//raii
	ScopdePtr(T *ptr) :_ptr(ptr){}
	~ScopdePtr()
	{
		delete _ptr;
	}
	//智能指针的特性
	T& operator*()
	{
		return *_ptr;
	}
	T operator->()
	{
		return *_ptr;
	}
private://注意声明为私有的
	ScopdePtr(ScopdePtr<T>&);//放拷贝,只声明不定义,就无法调用拷贝哦
	ScopdePtr<T> operator=(ScopdePtr<T>&);
private:
		T *_ptr;
	//解决拷贝的问题,防拷贝
};
void testScopdePtr()
{
	ScopdePtr<int> sp1(new int(10));
	ScopdePtr<int> sp2(sp1);
}
int main()
{
	testScopdePtr();
	return 0;
}
2.scoped array

注意这个里面的operator*和operator->就没什么用了

template<class T>
class ScopdeArray
{
public:
	//raii
	ScopdeArray(T *ptr) :_ptr(ptr){}
	~ScopdeArray()
	{
		delete [] _ptr;
	}
	//智能指针的特性
	/*T& operator*()
	{
		return *_ptr;
	}
	T operator->()
	{
		return *_ptr;
	}*/
	 T &operator[](size_t pos)
	{
		return _ptr[pos];
	}
private://注意声明为私有的
	ScopdeArray(ScopdeArray<T>&);//放拷贝,只声明不定义,就无法调用拷贝哦
	ScopdeArray<T> operator=(ScopdeArray<T>&);
private:
	T *_ptr;
	//解决拷贝的问题,防拷贝
};
void testScopdeArray()
{
	ScopdeArray<int> sp1(new int(10));
	ScopdeArray<int> sp2(sp1);
}
int main()
{
	testScopdeArray();
	return 0;
}
3.shared ptr

template<class T>
class SharedPtr
{
public:
	SharedPtr(T *ptr)
		:_ptr(ptr)
		, refCount(new int(1)){}
	~SharedPtr()
	{
		if (--(*refCount) == 0)
		{
			delete _ptr;
			delete refCount;
		}
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
	SharedPtr(SharedPtr<T> &sp)
		:_ptr(sp._ptr)
		, refCount(sp.refCount)
	{
		++(*refCount);
	}
	SharedPtr& operator=(const SharedPtr<T> &sp)
	{
		if (_ptr != sp._ptr)
		{
			if (--(*refCount) == 0)
			{
				delete _ptr;
				delete refCount;
			}
			_ptr = sp._ptr;
			refCount = sp.refCount;
			++(*refCount);
		}
	}
private:
	T *_ptr;
	int *refCount;
};
void testSharedPtr()
{
	SharedPtr<int > sp1(new int(10));
	SharedPtr<int> sp2(sp1);
}
int main()
{
	testSharedPtr();
		return 0;
}
4.shared array

template<class T>
class SharedArray
{
public:
	SharedArray(T *ptr)
		:_ptr(ptr)
		, refCount(new int(1)){}
	void release()
	{
		if (--(*refCount) == 0)
		{
			delete[] _ptr;
			delete refCount;
		}
	}
	~SharedArray()
	{
		release();
	}
	SharedArray(SharedArray<T> &sp)
		:_ptr(sp._ptr)
		, refCount(sp.refCount)
	{
		++(*refCount);
	}
	SharedArray& operator=(const SharedArray<T> &sp)
	{
		if (_ptr != sp._ptr)
		{
			release();
			_ptr = sp._ptr;
			refCount = sp.refCount;
			++(*refCount);
		}
		return *this;
	}
	T& operator[](size_t pos)
	{
		return _ptr[pos];
	}
private:
	T *_ptr;
	int *refCount;
};
5.shared中的循环引用

看如下代码:

template<class T>
class SharedPtr
{
public:
	SharedPtr(T *ptr=NULL)
		:_ptr(ptr)
		, refCount(new int(1)){}
	void release()
	{
		if (--(*refCount) == 0)
		{
			if (_ptr == NULL)
				return;
			printf("0x%p\n", _ptr);
			delete _ptr;
			delete refCount;
		}
	}
	~SharedPtr()
	{
		release();
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
	SharedPtr(SharedPtr<T> &sp)
		:_ptr(sp._ptr)
		, refCount(sp.refCount)
	{
		++(*refCount);
	}
	SharedPtr<T>& operator=(const SharedPtr<T> &sp)
	{
		if (_ptr != sp._ptr)
		{
			release();
			_ptr = sp._ptr;
			refCount = sp.refCount;
			++(*refCount);
		}
		return *this;
	}
private:
	T *_ptr;
	int *refCount;
};
template<class T>
class weakptr
{
public:
	weakptr() :_ptr(NULL)
	{}
	weakptr(const SharedPtr<T>& sp) :_ptr(sp._ptr){}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
private:
	T *_ptr;
};
struct ListNode
{
	SharedPtr<ListNode> _perv;
	SharedPtr<ListNode> _next;
};
void testcycle()
{
	SharedPtr<ListNode> cur = new ListNode;
	SharedPtr<ListNode> next = new ListNode;
	cur->_next = next;
	next->_perv = cur;
}
int main()
{
	testcycle();
	system("pause");
	return 0;
}
重点看这段代码:

struct ListNode
{
	SharedPtr<ListNode> _perv;
	SharedPtr<ListNode> _next;
};
void testcycle()
{
	SharedPtr<ListNode> cur = new ListNode;
	SharedPtr<ListNode> next = new ListNode;
		cur->_next= next;
		next->_perv= cur;
}
int main()
{
	testcycle();
	system("pause");
	return 0;
}
这里就产生了循环引用

在这注意:


  内存并未释放

解决方法就是用weakptr

代码如下:

template<class T>
class weakptr
{
public:
	weakptr() :_ptr(NULL)
	{}
	weakptr(const SharedPtr<T>& sp) :_ptr(sp._ptr){}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
private:
	T *_ptr;
};
其作用只是托管指针空间不进行操作

用法如下代码

template<class T>
class weakptr;
template<class T>
class SharedPtr
{
public:
	friend class weakptr<T>;
	SharedPtr(T *ptr=NULL)
		:_ptr(ptr)
		, refCount(new int(1)){}
	void release()
	{
		if (--(*refCount) == 0)
		{
			if (_ptr == NULL)
				return;
			printf("0x%p\n", _ptr);
			delete _ptr;
			delete refCount;
		}
	}
	~SharedPtr()
	{
		release();
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
	SharedPtr(SharedPtr<T> &sp)
		:_ptr(sp._ptr)
		, refCount(sp.refCount)
	{
		++(*refCount);
	}
	SharedPtr<T>& operator=(const SharedPtr<T> &sp)
	{
		if (_ptr != sp._ptr)
		{
			release();
			_ptr = sp._ptr;
			refCount = sp.refCount;
			++(*refCount);
		}
		return *this;
	}
private:
	T *_ptr;
	int *refCount;
};
template<class T>
class weakptr
{
public:
	weakptr() :_ptr(NULL)
	{}
	weakptr(const SharedPtr<T>& sp) :_ptr(sp._ptr){}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
private:
	T *_ptr;
};
struct ListNode
{
	weakptr<ListNode> _perv;
	weakptr<ListNode> _next;
};
void testcycle()
{
	SharedPtr<ListNode> cur = new ListNode;
	SharedPtr<ListNode> next = new ListNode;
	cur->_next = next;
	next->_perv = cur;
}
int main()
{
	testcycle();
	system("pause");
	return 0;
}


这样就释放了,解决了循环引用的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值