为什么需要智能指针?

为什么需要智能指针?

  1. 解决忘记释放内存导致内存泄漏的问题。
  2. 解决异常安全问题。
#include
using namespace std;

int div()
{
	int a, b;
	cin >> a >> b;
	if (b == 0)
		throw invalid_argument("除0错误");
	return a / b;
}
void Func()
{
	// 1、如果p1这里new 抛异常会如何?
	// 2、如果p2这里new 抛异常会如何?
	// 3、如果div调用这里又会抛异常会如何?
	int* p1 = new int;
	int* p2 = new int;
	cout << div() << endl;
	delete p1;
	delete p2;
}
int main()
{
	try
	{
		Func();
	}
	catch (exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

问:如果p1这里new 抛异常会如何?

答:p1、p2不会开空间,内存没有释放

问:如果p2这里new 抛异常会如何?

答:p2不会开空间,内存没有得到释放

问:如果div调用这里又会抛异常会如何?

答:内存没有被释放。

那么如何解决呢?

可以利用智能指针来解决这个问题。

智能指针的使用及其原理

RAII

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内 存、文件句柄、网络连接、互斥量等等)的简单技术。

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在 对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做 法有两大好处:

  • 不需要显示地释放资源。
  • 采用这种方式,对象所需的资源在其生命期内始终保持有效。

我们用一个类来封装一下这个指针,实现如下:

#include
using namespace std;

namespace hayaizo
{
	template<class T>
	class smart_ptr
	{
	public:
		smart_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{}

		~smart_ptr()
		{
			if (_ptr)
			{
				cout << "delete: " << _ptr << endl;
				delete _ptr;
			}
		}
	private:
		T* _ptr;
	};
}

int div()
{
	int a, b;
	cin >> a >> b;
	if (b == 0)
		throw invalid_argument("除0错误");
	return a / b;
}
void Func()
{
	hayaizo::smart_ptr<int> sp1(new int);
	hayaizo::smart_ptr<int> sp2(new int);
	cout << div() << endl;
}

int main(void)
{
	try {
		Func();
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}

	return 0;
}

运行结果:

为什么需要智能指针?_第1张图片

既然是指针,那么也需要支持解引用*->,重载这两个符号就好了。

namespace hayaizo
{
	template<class T>
	class smart_ptr
	{
	public:
		smart_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{}

		~smart_ptr()
		{
			if (_ptr)
			{
				cout << "delete: " << _ptr << endl;
				delete _ptr;
			}
		}

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

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

但这样地封装会有一个致命的缺点,一个地址只能被一个智能指针指向,不然会导致同一块内存释放两次的问题,我们看看官方库中的auto_ptr是怎么解决的。

为什么需要智能指针?_第2张图片

为什么需要智能指针?_第3张图片

可以看到,sp1的地址变成了sp2的地址了,然后sp1的地址变成了nullptr

手搓一个低配版的auto_ptr

namespace hayaizo
{
	template<class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{}

		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;
				}

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

		~auto_ptr()
		{
			if (_ptr)
			{
				cout << "delete: " << _ptr << endl;
				delete _ptr;
			}
		}

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

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

为什么需要智能指针?_第4张图片

为什么需要智能指针?_第5张图片

但这样确实可以解决问题,但是已经失去了原生指针的功能了,原生指针是支持同一个地址被很多个指针指向的,在介绍解决方法之前得先介绍unique_ptr

unique_ptr

unique_ptr非常粗暴,直接不让你拷贝,把拷贝构造禁掉了。

	template<class T>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{}

		unique_ptr(unique_ptr<T>& ap)=delete
		{}

		unique_ptr<T> operator=(unique_ptr<T>& ap)=delete
		{}

		~unique_ptr()
		{
			if (_ptr)
			{
				cout << "delete: " << _ptr << endl;
				delete _ptr;
			}
		}

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

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

为什么需要智能指针?_第6张图片

shared_ptr

shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。

  1. shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共 享。
  2. 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减 一。
  3. 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源。
  4. 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对 象就成野指针了。

比如我用三个智能指针指向地址0x00112233,因此,计数为3,当计数等于0的时候再进行销毁。

那么,这里的计数可以单纯用一个int _cnt或者static int _cnt来表示吗?

答案是否定的。

如果是int _cnt,那么每个对象都是单独的计数。

如果是``static int _cnt`,那么每个对象都是用的同一份计数。

所以这里需要用一个指针来表示计数。

template<class T>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			,_cnt(new int(1))
		{}

		shared_ptr(shared_ptr<T>& ap)
			:_ptr(ap._ptr)
			,_cnt(ap._cnt)
		{}

		void Release()
		{
			if (--(*_cnt) == 0)
			{
				cout << "delete: " << _ptr << endl;
				delete _ptr;
			}
		}

		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			//p1=p1的情况
			if (_ptr == sp._ptr)
			{
				return *this;
			}

			Release();
			
			_ptr = sp._ptr;
			_cnt = sp._cnt;
			(*_cnt)++;
			return *this;
		}

		int use_count()
		{
			return *_cnt;
		}

		T* get() const
		{
			return _ptr;
		}

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

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

		~shared_ptr()
		{
			Release();
		}

	private:
		T* _ptr;
		int* _cnt;
	};

}

为什么需要智能指针?_第7张图片

循环引用

为什么需要智能指针?_第8张图片

这里的n1n2的引用计数都是2,所以形成了相互制约的局面。

n1的销毁看n2n2的销毁看n1

为什么需要智能指针?_第9张图片

weak_ptr

weak_ptr是为配合shared_ptr而引入的一种智能指针。

weak_ptr可以从一个shared_ptr或另一个weak_ptr对象构造,它的构造和析构不会引起shared_ptr引用记数的增加或减少

	template<class T>
	class weak_ptr
	{
	public:
		weak_ptr()
			:_ptr(nullptr)
		{}

		weak_ptr(const shared_ptr<T>& sp)
			:_ptr(sp.get())
		{}

		weak_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			_ptr = sp.get();
			return *this;
		}

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

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

	private:
		T* _ptr;
	};

struct Node
{
	int _val;

	hayaizo::weak_ptr<Node> _next;
	hayaizo::weak_ptr<Node> _prev;

	~Node()
	{
		cout << "~Node" << endl;
	}
};


int main(void)
{
	hayaizo::shared_ptr<Node> n1(new Node);
	hayaizo::shared_ptr<Node> n2(new Node);
	
	n1->_next = n2;
	n2->_prev = n1;


	return 0;
}

为什么需要智能指针?_第10张图片

很简单,就是n1内部的指针不参与引用计数,用另外的类封装起来就好了,就不会动shared_ptr里面的引用计数了。

定制删除器

其实就是个仿函数,可以自己传删除方案。

//默认删除器
	template<class T>
	struct Delete
	{
		void operator()(T* ptr)
		{
			cout << "delete: " << ptr << endl;
			delete ptr;
		}
	};

	template<class T,class D=Delete<T>>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			,_cnt(new int(1))
		{}

		shared_ptr(shared_ptr<T>& ap)
			:_ptr(ap._ptr)
			,_cnt(ap._cnt)
		{}

		void Release()
		{
			if (--(*_cnt) == 0)
			{
				cout << "delete: " << _ptr << endl;
				D del;
				del(_ptr);
				//D()(_ptr);匿名对象调用()
			}
		}

		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			//p1=p1的情况
			if (_ptr == sp._ptr)
			{
				return *this;
			}

			Release();
			
			_ptr = sp._ptr;
			_cnt = sp._cnt;
			(*_cnt)++;
			return *this;
		}

		int use_count()
		{
			return *_cnt;
		}

		T* get() const
		{
			return _ptr;
		}

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

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

		~shared_ptr()
		{
			Release();
		}

	private:
		T* _ptr;
		int* _cnt;
	};
template<class T>
struct DeleteArray
{
	void operator()(T* ptr)
	{
		cout << "delete[]" << ptr << endl;
		delete[] ptr;
	}
};

template<class T>
struct Free
{
	void operator()(T* ptr)
	{
		cout << "free" << ptr << endl;
		free(ptr);
	}
}; 

为什么需要智能指针?_第11张图片

总代码:

//#include
//using namespace std;
//
//int div()
//{
//	int a, b;
//	cin >> a >> b;
//	if (b == 0)
//		throw invalid_argument("除0错误");
//	return a / b;
//}
//void Func()
//{
//	// 1、如果p1这里new 抛异常会如何?
//	// 2、如果p2这里new 抛异常会如何?
//	// 3、如果div调用这里又会抛异常会如何?
//	int* p1 = new int;
//	int* p2 = new int;
//	cout << div() << endl;
//	delete p1;
//	delete p2;
//}
//int main()
//{
//	try
//	{
//		Func();
//	}
//	catch (exception& e)
//	{
//		cout << e.what() << endl;
//	}
//	return 0;
//}

#include
using namespace std;

namespace hayaizo
{
	template<class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{}

		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;
				}

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

		~auto_ptr()
		{
			if (_ptr)
			{
				cout << "delete: " << _ptr << endl;
				delete _ptr;
			}
		}

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

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

	template<class T>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{}

		unique_ptr(unique_ptr<T>& ap)=delete
		{}

		unique_ptr<T> operator=(unique_ptr<T>& ap)=delete
		{}

		~unique_ptr()
		{
			if (_ptr)
			{
				cout << "delete: " << _ptr << endl;
				delete _ptr;
			}
		}

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

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

	//默认删除器
	template<class T>
	struct Delete
	{
		void operator()(T* ptr)
		{
			cout << "delete: " << ptr << endl;
			delete ptr;
		}
	};

	template<class T,class D=Delete<T>>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			,_cnt(new int(1))
		{}

		shared_ptr(shared_ptr<T>& ap)
			:_ptr(ap._ptr)
			,_cnt(ap._cnt)
		{}

		void Release()
		{
			if (--(*_cnt) == 0)
			{
				cout << "delete: " << _ptr << endl;
				D del;
				del(_ptr);
				//D()(_ptr);匿名对象调用()
			}
		}

		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			//p1=p1的情况
			if (_ptr == sp._ptr)
			{
				return *this;
			}

			Release();
			
			_ptr = sp._ptr;
			_cnt = sp._cnt;
			(*_cnt)++;
			return *this;
		}

		int use_count()
		{
			return *_cnt;
		}

		T* get() const
		{
			return _ptr;
		}

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

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

		~shared_ptr()
		{
			Release();
		}

	private:
		T* _ptr;
		int* _cnt;
	};

	template<class T>
	class weak_ptr
	{
	public:
		weak_ptr()
			:_ptr(nullptr)
		{}

		weak_ptr(const shared_ptr<T>& sp)
			:_ptr(sp.get())
		{}

		weak_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			_ptr = sp.get();
			return *this;
		}

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

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

	private:
		T* _ptr;
	};

}

struct Node
{
	int _val;

	hayaizo::weak_ptr<Node> _next;
	hayaizo::weak_ptr<Node> _prev;

	~Node()
	{
		cout << "~Node" << endl;
	}
};

template<class T>
struct DeleteArray
{
	void operator()(T* ptr)
	{
		cout << "delete[]" << ptr << endl;
		delete[] ptr;
	}
};

template<class T>
struct Free
{
	void operator()(T* ptr)
	{
		cout << "free" << ptr << endl;
		free(ptr);
	}
}; 


int main(void)
{
	/*hayaizo::shared_ptr n1(new Node);
	hayaizo::shared_ptr n2(new Node);
	
	n1->_next = n2;
	n2->_prev = n1;*/
	hayaizo::shared_ptr<Node, DeleteArray<Node>> n1(new Node[5]);
	hayaizo::shared_ptr<Node> n2(new Node);
	hayaizo::shared_ptr<int, DeleteArray<int>> n3(new int[5]);
	hayaizo::shared_ptr<int, Free<int>> n4((int*)malloc(sizeof(int)));


	return 0;
}

你可能感兴趣的:(c++,算法,开发语言,智能指针)