智能指针——浅析

智能指针

本人不才,只能将智能指针介绍一下,无法结合线程进行深入探索

介绍及作用

在异常产生进行跳转时,通过栈帧回收进行内存释放,防止内存泄漏

基于RAII思想可以创建出只能指针

RAII(Resource Acquisition Is Initialization)------是一种利用对象控制空间生命周期的技术

这种技术好处就在于可以自动化的释放资源

智能指针------使用如指针,支持->和**针对内置数据类型->针对自定义数据类型


先介绍一个对象管理一份资源

auto_ptr
cpp 复制代码
	template<class T>
	class auto_ptr
	{
		// auto_ptr 会产生指针悬空的情况,在进行解引用的时候很危险
	public:
		auto_ptr(T* p=nullptr) :_ptr(p) {}
		~auto_ptr()
		{
			puts("~auto_ptr()");
			delete _ptr;
		}
		auto_ptr(auto_ptr<T>& p)
		{
			_ptr = p._ptr;
			p._ptr = nullptr;
		}
		auto_ptr<T>& operator=(auto_ptr<T>& p)
		{
			// 因为是一个对象管理一份资源,比较的时候也可以使用this!=&p
			if (_ptr != p._ptr)
			{
				if (_ptr) delete _ptr;
				_ptr = p._ptr;
				p._ptr = nullptr;
			}
			return *this;
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};

auto_ptr会在赋值的时候将资源进行转移,并将自己置为nullptr,从而造成指针悬空的问题

unique_ptr
cpp 复制代码
	template<class T>
	class unique_ptr
	{
		// 在auto_ptr的基础上进行优化,防拷贝
	public:
		unique_ptr(T* p = nullptr) :_ptr(p) {}
		~unique_ptr()
		{
			puts("~auto_ptr()");
			delete _ptr;
		}
		unique_ptr(const unique_ptr<T>& p) = delete;
		unique_ptr& operator=(const unique_ptr<T>& p) = delete;
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	private:
		// unique_ptr(const unique_ptr<T>& p);
		// unique_ptr& operator=(const unique_ptr<T>& p);
		T* _ptr;
	};

只是为了解决拷贝带来的指针悬空的问题------禁用拷贝构造和赋值重载

两种方式达到禁用拷贝构造和赋值重载

  1. 将拷贝构造和赋值重载定义成private
  2. 由于C++11扩展了delete的功能,可以在public中对两个函数使用delete关键字修饰

多个指针同时享有一份资源

shared_ptr

使用一个计数指针进行计数
我的代码写成这个样子是因为考虑到可能只声明指针但是没有赋值的情况,所以就需要对指针位nullptr的情况进行特殊判断

cpp 复制代码
struct ListNode
{
	int val;
	bit::shared_ptr<ListNode> next;
	bit::shared_ptr<ListNode> prev;

	~ListNode()
	{
		cout << "~ListNode()" << endl;
	}
};
	template<class T>
	class shared_ptr	
	{
		// 删除器就是为了解决:释放数组,不是new出来的指针
	public:
		shared_ptr(T* p = nullptr) :_ptr(p), _count(new int(0)) {}
		template<class D>
		shared_ptr(T* p,D del)
			:_ptr(p),
			_count(new int(1)),
			_del(del)
		{
			if (p) (*_count)++;
		}
		~shared_ptr()
		{
			if (_ptr)
			{
				//printf("~shared_ptr() -> %p\n", _ptr);

				if (--(*_count) == 0)
				{
					delete _count;
				}
				delete _ptr;
			}
			if (_ptr == nullptr) delete _count;
		}
		shared_ptr(const shared_ptr<T>& p)
		{
			if (_ptr && _ptr != p._ptr)
			{
 				if (--(*_count) == 0) _count = p._count;
				(*_count)++;
				_ptr = p._ptr;
			}
			else
			{
				// nullptr / 有值相等
				_ptr = p._ptr;
				_count = p._count;
				//*_count++; // ++优先级大于*,最好不要写这种代码
				(*_count)++;
			}
		}
		shared_ptr& operator=(const shared_ptr<T>& p)
		{
			if (_ptr && _ptr != p._ptr)
			{
				if (--(*_count) == 0) _count = p._count;
				(*_count)++;
				_ptr = p._ptr;
			}
			else
			{
				// nullptr / 有值相等
				_ptr = p._ptr;
				_count = p._count;
				(*_count)++;
			}
			return *this;
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		int use_count() const 
		{
			return *_count;
		}
		T* get() const
		{
			return _ptr;
		}
	private:
		T* _ptr;
		int* _count;// 引入计数指针
		function<void(T*)> _del = [](T* p) {delete p; };
	};


这里会产生循环引用的问题

为了解决这个问题有了weak_ptr


weak_ptr

weak_ptr只进行引用不进行计数

cpp 复制代码
struct ListNode
{
	int val;
	bit::weak_ptr<ListNode> next;
	bit::weak_ptr<ListNode> prev;

	~ListNode()
	{
		cout << "~ListNode()" << endl;
	}
};
	template<class T>
	class weak_ptr
	{
		// 不增加引用计数
	public:
		weak_ptr() :_ptr(nullptr) {}
		~weak_ptr()
		{
			//printf("~shared_ptr() -> %p\n", _ptr);
		}
		weak_ptr(const shared_ptr<T>& p)
		{
			_ptr = p.get();
		}
		weak_ptr& operator=(const shared_ptr<T>& p)
		{
			_ptr = p.get();
			return *this;
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};

使用包装器进行释放

这里还有一个问题------如何根据空间开的个数进行释放呢,数组和指针释放的方式是不一样的

也就是在share_ptr看不懂的版本

cpp 复制代码
template<class T>
struct Del
{
	void operator()(T* ptr)
	{
		delete[] ptr;
	}
};
	template<class T>
	class shared_ptr	
	{
		// 删除器就是为了解决:释放数组,不是new出来的指针
	public:
		shared_ptr(T* p = nullptr) :_ptr(p), _count(new int(0)) {}
		template<class D>
		shared_ptr(T* p,D del)
			:_ptr(p),
			_count(new int(1)),
			_del(del)
		{
			if (p) (*_count)++;
		}
		~shared_ptr()
		{
			if (_ptr)
			{
				//printf("~shared_ptr() -> %p\n", _ptr);

				if (--(*_count) == 0)
				{
					delete _count;
				}
				delete _ptr;
			}
			if (_ptr == nullptr) delete _count;
		}
		shared_ptr(const shared_ptr<T>& p)
		{
			if (_ptr && _ptr != p._ptr)
			{
 				if (--(*_count) == 0) _count = p._count;
				(*_count)++;
				_ptr = p._ptr;
			}
			else
			{
				// nullptr / 有值相等
				_ptr = p._ptr;
				_count = p._count;
				//*_count++; // ++优先级大于*,最好不要写这种代码
				(*_count)++;
			}
		}
		shared_ptr& operator=(const shared_ptr<T>& p)
		{
			if (_ptr && _ptr != p._ptr)
			{
				if (--(*_count) == 0) _count = p._count;
				(*_count)++;
				_ptr = p._ptr;
			}
			else
			{
				// nullptr / 有值相等
				_ptr = p._ptr;
				_count = p._count;
				(*_count)++;
			}
			return *this;
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		int use_count() const 
		{
			return *_count;
		}
		T* get() const
		{
			return _ptr;
		}
	private:
		T* _ptr;
		int* _count;// 引入计数指针
		function<void(T*)> _del = [](T* p) {delete p; };
	};

仿函数,函数指针,lambda可以使用包装器------不论使用哪一种,实现的目的就是为了释放数组

他的类型一定是void(*T)

为什么需要在声明的时候就给默认到lambda------如果在这个指针是拷贝构造生成的,那还得进行包装器拷贝,同样在赋值重载的地方也需要同样的操作,还不如声明的时候给默认值

相关推荐
A懿轩A1 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
机器视觉知识推荐、就业指导1 小时前
C++设计模式:享元模式 (附文字处理系统中的字符对象案例)
c++
半盏茶香1 小时前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
Ronin3052 小时前
11.vector的介绍及模拟实现
开发语言·c++
✿ ༺ ོIT技术༻2 小时前
C++11:新特性&右值引用&移动语义
linux·数据结构·c++
字节高级特工2 小时前
【C++】深入剖析默认成员函数3:拷贝构造函数
c语言·c++
唐诺8 小时前
几种广泛使用的 C++ 编译器
c++·编译器
冷眼看人间恩怨9 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
红龙创客9 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin9 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin