IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> C++(19)——智能指针shared_ptr -> 正文阅读

[游戏开发]C++(19)——智能指针shared_ptr

shared_ptr的概念

shared_ptr实现共享式拥有(shared ownership)概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用(reference)被销毁”时候释放。

基本原理

智能指针是(几乎总是)模板类,shared_ptr 同样是模板类,所以在创建 shared_ptr 时需要指定其指向的类型。shared_ptr 负责在不使用实例时释放由它管理的对象,同时它可以自由的共享它指向的对象。
shared_ptr 使用经典的 “引用计数” 的方法来管理对象资源。

模拟实现理解原理

在系统提供的共享性智能指针中,存在一个观察器use_count用来返回shared_ptr所管理对象的引用计数,使用样例如下:
在这里插入图片描述

那么,其具体实现是怎样的呢?我们来模仿源码实现一下:

template<class _Ty>
class RefCnt
{
public:
	_Ty* mptr;
	//int ref;
	std::atomic_int ref;

public:
	RefCnt(_Ty* p = nullptr) :mptr(p), ref(mptr != nullptr) { //ref = mptr == nullptr?0:1;}
	~RefCnt() {}
};
template<class _Ty, class _Dx = MyDeletor<_Ty> >
class my_shared_ptr // thread;
{
public:
	my_shared_ptr(_Ty* p = nullptr) :ptr(nullptr)
	{
		if (p != nullptr)
		{
			ptr = new RefCnt(p);
		}
	}
	my_shared_ptr(const my_shared_ptr& _Y) :ptr(_Y.ptr)
	{
		if (ptr != nullptr)
		{
			ptr->ref += 1;
		}
	}// my_shared_ptr<Object> op2(op1);
	my_shared_ptr(my_shared_ptr&& _Y) :ptr(_Y.ptr)
	{
		_Y.ptr = nullptr;
	}// my_shared_ptr<Object> op2(std::move(op1));
	operator bool() const { return ptr != nullptr; }

	my_shared_ptr& operator=(const my_shared_ptr& _Y) // 
	{
		if (this == &_Y || this->ptr == _Y.ptr) return *this;
		if (ptr != NULL && --ptr->ref == 0)
		{
			mDeletor(ptr);
		}
		ptr = _Y.ptr;
		if (ptr != nullptr)
		{
			ptr->ref += 1;
		}
		return *this;
	}
	my_shared_ptr& operator=(my_shared_ptr&& _Y) // move operator =
	{
		if (this == &_Y) return *this;
		if (this->ptr == _Y.ptr && this->ptr != nullptr && _Y.ptr != nullptr)
		{
			this->ptr->ref -= 1;
			_Y.ptr = nullptr;
			return * this;
		}
		if (this->ptr != nullptr && --ptr->ref == 0)
		{
			mDeletor(ptr);
		}
		ptr = _Y.ptr;
		_Y.ptr = nullptr;
		return *this;
	}
	void reset(_Ty* p = nullptr)
	{
		if (this->ptr != nullptr && --this->ptr->ref == 0)
		{
			mDeletor(ptr);
		}
		ptr = new RefCnt<_Ty>(p);
	}
	~my_shared_ptr()
	{
		if (this->ptr != nullptr && --this->ptr->ref == 0)
		{
			mDeletor(ptr->mptr);
			delete ptr;
		}
		ptr = nullptr;
	}
	_Ty* get() const { return ptr->mptr; }
	_Ty& operator*() const
	{
		return *get();
	}
	_Ty* operator->() const
	{
		return get();
	}

	size_t use_count() const
	{
		if (this->ptr == nullptr) return 0;
		return this->ptr->ref;
	}
	void swap(my_shared_ptr& r)
	{
		std::swap(this->ptr, r.ptr);
	}

private:
	RefCnt<_Ty>* ptr;
	_Dx mDeletor;
};

template<class _Ty, class _Dx >
class my_shared_ptr<_Ty[],_Dx>
{
public:
	my_shared_ptr(_Ty* p = nullptr) :ptr(nullptr)
	{
		if (p != nullptr)
		{
			ptr = new RefCnt(p);
		}
	}
	my_shared_ptr(const my_shared_ptr& _Y) :ptr(_Y.ptr)
	{
		if (ptr != nullptr)
		{
			ptr->ref += 1;
		}
	}// my_shared_ptr<Object> op2(op1);
	my_shared_ptr(my_shared_ptr&& _Y) :ptr(_Y.ptr)
	{
		_Y.ptr = nullptr;
	}// my_shared_ptr<Object> op2(std::move(op1));
	operator bool() const { return ptr != nullptr; }

	my_shared_ptr& operator=(const my_shared_ptr& _Y) // 
	{
		if (this == &_Y || this->ptr == _Y.ptr) return *this;
		if (ptr != NULL && --ptr->ref == 0)
		{
			mDeletor(ptr->mptr);
			delete ptr;
		}
		ptr = _Y.ptr;
		if (ptr != nullptr)
		{
			ptr->ref += 1;
		}
		return *this;
	}
	my_shared_ptr& operator=(my_shared_ptr&& _Y) // move operator =
	{
		if (this == &_Y) return *this;
		if (this->ptr == _Y.ptr && this->ptr != nullptr && _Y.ptr != nullptr)
		{
			this->ptr->ref -= 1;
			_Y.ptr = nullptr;
			return * this;
		}
		if (this->ptr != nullptr && --ptr->ref == 0)
		{
			mDeletor(ptr->mptr);
			delete ptr;
		}
		ptr = _Y.ptr;
		_Y.ptr = nullptr;
		return *this;
	}
	void reset(_Ty* p = nullptr)
	{
		if (this->ptr != nullptr && --this->ptr->ref == 0)
		{
			mDeletor(ptr->mptr);
			delete ptr;
		}
		ptr = new RefCnt<_Ty>(p);
	}
	~my_shared_ptr()
	{
		if (this->ptr != nullptr && --this->ptr->ref == 0)
		{
			mDeletor(ptr->mptr);
			delete ptr;
		}
		ptr = nullptr;
	}
	_Ty* get() const { return ptr->mptr; }
	_Ty& operator*() const
	{
		return *get();
	}
	_Ty* operator->() const
	{
		return get();
	}

	size_t use_count() const
	{
		if (this->ptr == nullptr) return 0;
		return this->ptr->ref;
	}
	void swap(my_shared_ptr& r)
	{
		std::swap(this->ptr, r.ptr);
	}

	_Ty& operator[](const int idx) const
	{
		return ptr->mptr[idx];
	}

private:
	RefCnt<_Ty>* ptr;
	_Dx mDeletor;
};

根据代码:我们分析到对象的内存结构图如下:这有助于我们更好地理解:
在这里插入图片描述

相互引用问题

在这里插入图片描述
退出之前,它们的 use_count() 都为2,退出了 fun() 后,由于 c和 p 对象互相引用,它们的引用计数都是 1,不能自动释放(可以看到没有调用析构函数),并且此时这两个对象再无法访问到。这就引起了c++中那臭名昭著的“内存泄漏”。

我们解决这个问题就可以使用weak_ptr:
使用weak_ptr 来打破循环引用,它与一个 shared_ptr 绑定,但却不参与引用计数的计算,不论是否有 weak_ptr 指向,一旦最后一个指向对象的 shared_ptr 被销毁,对象就会被释放。weak_ptr 像是 shared_ptr 的一个助手。同时,在需要时,它还能摇身一变,生成一个与它绑定的 shared_ptr 共享引用计数的shared_ptr。

多线程下的shared_ptr存在的问题

因为在多线程下,多个线程很有可能对同一个shared_ptr的引用计数进行操作,然而在我们设计的shared_ptr中的引用计数为int类型,对于int的一个操作其实并不是原子操作,那么多线程环境下就不能保证安全的运行。
于是我们会对引用计数进行一个重新的设计:
引入#include<atomic>,修改引用计数的类型如下:
在这里插入图片描述
从而解决这一问题。

总结

(1) 智能指针主要的用途就是方便资源的管理,自动释放没有指针引用的资源。

(2) 使用引用计数来标识是否有多余指针指向该资源。(注意,shart_ptr本身指针会占1个引用)

(3) 在赋值操作中, 原来资源的引用计数会减一,新指向的资源引用计数会加一。

(4) 引用计数加一/减一操作是原子操作,所以线程安全的。

(5) make_shared要优于使用new,make_shared可以一次将需要内存分配好。(第一张图片中有解释)

(6) std::shared_ptr的大小是原始指针的两倍,因为它的内部有一个原始指针指向资源,同时有个指针指向引用计数。

(7) 引用计数是分配在动态分配的,std::shared_ptr支持拷贝,新的指针获可以获取前引用计数个数。

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-03-12 17:53:59  更:2022-03-12 17:54:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年2日历 -2025/2/23 12:55:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码