C++进阶--智能指针

发布于:2024-04-27 ⋅ 阅读:(22) ⋅ 点赞:(0)

智能指针的概念

智能指针是C++中的一个重要概念,用于管理动态分配的对象内存它是一个类模板,通过封装原始指针,并在对象生命周期结束时自动释放内存,从而避免了内存泄漏和资源管理的繁琐工作

C++标准库提供了多种常见的智能指针,目前常用的有:unique_ptr , shared_ptr , weak_ptr。(头文件: < memory >

为什么需要智能指针?

看一个例子:

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

在这里插入图片描述

RAll

RAII(Resource Acquisition Is Initialization)的核心思想是资源的获取和释放应该与对象的生命周期绑定在一起。当一个对象被创建时,它应该获取所需要的资源;当对象被销毁时,它应该释放已经获取的资源。这样可以确保资源在不再需要时被正确释放,从而避免资源泄漏。

利用RAll原理做出智能指针

代码:

template<class T>
class SmartPtr
{
public:
	//RALL
	SmartPtr(T* ptr)
		:_ptr(ptr)
	{}

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

	//解引用
	T& operator*()
	{
		return *_ptr;
	}
	//指针形式
	T* operator->()
	{
		return _ptr;
	}
private:
	T* _ptr;
};

利用RAll原理制作智能指针:
在这里插入图片描述
在这里插入图片描述

测试:在这里插入图片描述
在这里插入图片描述

auto_ptr

在这里插入图片描述

简单使用

在这里插入图片描述
在这里插入图片描述
由于这种智能指针不能对实际应用起到作用,所以现在大多数程序员都没有用到它。

要模拟实现一个auto_ptr时,只需要在上面代码的赋值拷贝中加上:
在这里插入图片描述

unique_ptr

unique_ptr与常规指针的一个主要区别就是:它拥有对指向对象的独占拥有权。这意味着同一时间只能有一个unique_ptr指向某个对象,不能进行复制操作,只能进行移动操作。当unique_ptr被销毁或重置时,他所指向的对象也会自动删除。

模拟实现

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

		unique_ptr(unique_ptr<T>& p)= delete;
		unique_ptr operator=(const unique_ptr<T>& p) = delete;

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

			delete _ptr;
		}

		// 像指针一样
		T& operator*()
		{
			return *_ptr;
		}

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

实现时只需要对默认拷贝构造和默认赋值构造给它删除了,那么就实现了不能对它进行复制了。

测试:
在这里插入图片描述

shared_ptr

在这里插入图片描述

为什么不能用static成员来进行计数?

在这里插入图片描述

模拟实现

template<class T>
	class shared_ptr
	{
	public:
		//常规使用的构造函数
		shared_ptr(T* ptr = nullptr)
			: _ptr(ptr),
			_count(new int(1))
		{}

		//sp2(sp1)
		shared_ptr(const shared_ptr<T>& sp)
		{
			_ptr = sp._ptr;
			_count = sp._count;

			//拷贝后将count+1
			++(*_count);
		}

		//sp3=sp1
		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			if (_ptr != sp._ptr)
			{
				//使用赋值时,要考虑之前的智能指针指向的空间
				release();
				_ptr = sp._ptr;
				_count = sp._count;
				//拷贝后将count+1
				++(*_count);
			}

			return *this;
		}
		//释放资源
		void release()
		{
			if (--(*_count) == 0)
			{
				cout << "delete:" << _ptr << endl;
				delete _ptr;
				delete _count;
			}
		}
		~shared_ptr()
		{
			//当count为0时触发
			release();
		}

		int use_count()
		{
			return *_count;
		}

		// 像指针一样
		T& operator*()
		{
			return *_ptr;
		}

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

		T* get() const
		{
			return _ptr;
		}
	private:
		T* _ptr;
		int* _count;
	};

解释:
在这里插入图片描述

测试:
在这里插入图片描述

赋值时必须要对之前智能指针指向的空间进行检查:
在这里插入图片描述

验证解引用:
在这里插入图片描述

定制删除器

在这里插入图片描述
在类中添加一个成员:
在这里插入图片描述
在这里插入图片描述
添加一个构造函数的重载:
在这里插入图片描述
在这里插入图片描述
释放时的修改:
在这里插入图片描述

template <class T>
struct ArrayDelete
{
	void operator()(T* ptr)
	{
		delete[] ptr;
	}
};
//定制删除器
int main()
{
	fnc::shared_ptr<ListNode> n(new ListNode(10));
 	fnc::shared_ptr<ListNode> n1(new ListNode[10],ArrayDelete<ListNode>());
	fnc::shared_ptr<FILE> n2(fopen("FileName.cpp", "r"), [](FILE* file) {fclose(file); });
}

在这里插入图片描述

智能指针的缺陷

struct ListNode
{
	int _val;

	fnc::shared_ptr<ListNode> _next;
	fnc::shared_ptr<ListNode> _prev;
	
	ListNode(int val=0)
		:_val(val)
	{}
};

int main()
{
	fnc::shared_ptr<ListNode> n1(new ListNode(10));
	fnc::shared_ptr<ListNode> n2(new ListNode(20));

	cout << n1.use_count() << endl;
	cout << n2.use_count() << endl;

	n1->_next = n2;
	n2->_prev = n1;

	cout << n1.use_count() << endl;
	cout << n2.use_count() << endl;

	//delete n1;
	//delete n2;

	return 0;
}

结果:
在这里插入图片描述
在这里插入图片描述

weak_ptr

weak_ptr就是用于解决循环引用的问题。它与shared_ptr配合使用,可以指向一个由shared_ptr管理的对象,但不会增加该对象的引用计数。

weak_ptr不拥有所指对象的拥有权,当指向对象被释放时,weak_ptr会自动失效,不再指向有效的对象

	template<class T>
	class weak_ptr
	{
	public:
		// RAll
		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;
	};

将LIstNode的next指针和prev指针类型改为weak_ptr的:
在这里插入图片描述
结果:
在这里插入图片描述