首页 > 其他分享 >【CPP】智能指针

【CPP】智能指针

时间:2024-03-25 21:30:44浏览次数:20  
标签:SharedPtr int sp 智能 pcount ._ CPP ptr 指针

引言

智能指针是RAII思想的体现,有时候程序抛异常导致指针指向的内存资源未释放,造成内存泄漏,这时就需要用到智能指针,它可以出作用域自动调用析构函数释放内存资源

内存泄漏

什么是内存泄漏

什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。

解决方案

1.事前预防:采用RAII即智能指针
2.事后补救:用内存泄漏检测工具。

智能指针

template<class T>

class SmartPtr
{
public:
	SmartPtr(T* ptr)
		:_ptr(ptr)
	{}

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

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

	~SmartPtr()
	{
		delete _ptr;
	}

private:
	T* _ptr;
};

int div()
{
	int a, b;
	std::cin >> a >> b;
	if (b == 0)
		throw std::exception("division zero error");
	return a / b;
}

void Func()
{
	SmartPtr<int> sp1(new int);//把申请的资源交给sp管理
	SmartPtr<int> sp2(new int);
	std::cout << div() << std::endl;
}

int main()
{
	try
	{
		Func();
	}
	catch (std::exception& e)
	{
		std::cout << e.what() << std::endl;
	}
	return 0;
}

智能指针的拷贝构造

auto_ptr

采用管理权转移的方式避免浅拷贝同一空间被析构两次

auto_ptr(auto_ptr<T>& sp)
:_ptr(sp._ptr)
{
// 管理权转移
sp._ptr = nullptr;
}
auto_ptr<T>& operator=(auto_ptr<T>& ap)
{
// 检测是否为自己给自己赋值
if (this != &ap)
{
// 释放当前对象中资源
if (_ptr)
delete _ptr;
// 转移ap中资源到当前对象中
_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}

问题:

采用管理权转移方式,看似没问题,只让对象析构一次,另一个指针指向空。但会存在拷贝对象悬空问题,即再对拷贝对象解引用是对空指针解引用。

在这里插入图片描述

unique_ptr

采用delete关键直接禁止拷贝
在这里插入图片描述

shared_ptr

采用引用计数的方式,支持拷贝

template<class T>

class SharedPtr
{
public:
	SharedPtr(T* ptr)
		:_ptr(ptr)
		,_pcount(new int(1))
	{}

	SharedPtr(const SharedPtr<T>& sp)
		:_ptr(sp._ptr)
		,_pcount(sp._pcount)
	{
		AddCount();
	}

	SharedPtr<T>& operator=(const SharedPtr<T>& sp)
	{
		if (_ptr != sp._ptr)
		{
			Release();

			_ptr = sp._ptr;
			_pcount = sp._pcount;
			AddCount();
		}
		
		return *this;
	}

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

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

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

	void AddCount()
	{
		++(*_pcount);
	}

	~SharedPtr()
	{
		Release();
	}

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

void test1()
{
	SharedPtr<int> sp1(new int(1));
	SharedPtr<int> sp2(sp1);
	SharedPtr<int> sp4(new int(10));

	//sp4 = sp1;
	sp1 = sp4;
}
int main()
{
	test1();
	return 0;
}

多线程实现shared_ptr

template<class T>

class SharedPtr
{
public:
	SharedPtr(T* ptr)
		:_ptr(ptr)
		,_pcount(new int(1))
		,_lock(new std::mutex)
	{}

	SharedPtr(const SharedPtr<T>& sp)
		:_ptr(sp._ptr)
		,_pcount(sp._pcount)
		,_lock(sp._lock)
	{
		AddCount();
	}

	SharedPtr<T>& operator=(const SharedPtr<T>& sp)
	{
		if (_ptr != sp._ptr)
		{
			Release();

			_ptr = sp._ptr;
			_pcount = sp._pcount;
			_lock = sp._lock;
			AddCount();
		}
		
		return *this;
	}

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

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

	void Release()
	{
		_lock->lock();

		bool deleteflag = false;
		if (--(*_pcount) == 0)
		{
			std::cout << "delete:" << _ptr << std::endl;
			delete _ptr;
			delete _pcount;

			deleteflag = true;
		}
		_lock->unlock();

		if (deleteflag)
		{
			delete _lock;
		}
	}

	void AddCount()
	{
		_lock->lock();
		++(*_pcount);
		_lock->unlock();
	}

	T* Get()
	{
		return _ptr;
	}

	int use_count()
	{
		return *_pcount;
	}

	~SharedPtr()
	{
		Release();
	}


private:
	T* _ptr;
	int* _pcount;
	std::mutex* _lock;
};

void test1()
{
	SharedPtr<int> sp1(new int(1));
	SharedPtr<int> sp2(sp1);
	SharedPtr<int> sp4(new int(10));

	//sp4 = sp1;
	sp1 = sp4;
}

struct Date
{
	int _year;
	int _month;
	int _day;
};

void Func(SharedPtr<Date>& sp, size_t n, std::mutex& mtx)
{
	std::cout << sp.Get() << std::endl;

	for (size_t i = 0; i < n; i++)
	{
		SharedPtr<Date> copy(sp);
		mtx.lock();
		sp->_year++;
		sp->_day++;
		sp->_month++;
		mtx.unlock();
	}
	
}
void test2()
{
	SharedPtr<Date> p(new Date);
	std::cout << p.Get() << std::endl;
	const size_t n = 10000;
	std::mutex mtx;
	std::thread t1(Func, std::ref(p), n, std::ref(mtx));
	std::thread t2(Func, std::ref(p), n, std::ref(mtx));

	t1.join();
	t2.join();

	std::cout<<p.use_count()<<std::endl;
}

shared_ptr的循环引用问题

看下面这段代码

std::shared_ptr<ListNode> n1(new ListNode);
std::shared_ptr<ListNode> n2(new ListNode);

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

在这里插入图片描述
当node2析构时,引用计数变为1,因为还有node1的next指向它,node1析构时,其引用计数变为1,因为还有node2的_prev指向它。为何此时node2 的prev还在呢?因为node2的引用计数不为0,node2的prev要等node2析构才会释放,node2的析构又由node1的next决定。从而造成了循环引用,导致内存泄漏。

解决方案

1.把prev去掉,只保留next。即把双向链表改为单链表
2.weak_ptr

weak_ptr

不支持RAII,专门设计来辅助解决shared_ptr的循环引用问题

在这里插入图片描述
把ListNode类内对象定义为weak_ptr,不增加引用计数。就能完成对应的析构。

自定义删除器

struct Date
{
	int _year=0;
	int _month=0;
	int _day=0;
	~Date()
	{}
};
void test4()
{
	std::shared_ptr<Date> spa(new Date[10]);
}

new[]开空间时编译器会在数组开头多开4个字节,存要调用析构函数的个数。如果delete不加[]就会造成程序崩溃,因为删除位置不对

可调用对象

函数指针,函数对象,lambda表达式都可以。
在这里插入图片描述
这样就不是默认的delete释放,而是调用自定义的delete方法。
在这里插入图片描述
通过智能指针打开文件也会出现错误,因为shared_ptr默认调用的是delete方法,而关闭文件是fclose,因此也必须自定义删除器。

标签:SharedPtr,int,sp,智能,pcount,._,CPP,ptr,指针
From: https://blog.csdn.net/m0_73865858/article/details/136964618

相关文章

  • 代码随想录第一天-双指针+二分法
    参考资源:https://programmercarl.com/、ChatGPT3.5语言:Java二分法二分法,又称为二分查找或折半查找,是一种在有序数组中查找目标值的算法。它的基本思想是将目标值与数组中间的元素进行比较,若目标值等于中间元素,则查找成功;若目标值小于中间元素,则在数组的左半部分继续查......
  • 四 3745. 牛的学术圈 I (双指针|二分)
    四3745.牛的学术圈I(双指针|二分)思路:要到达最大h指数,此时有a1篇引用大于等于h,a2篇引用等于h-1,且满足a1+min(a2,L)大于等于h,a1、a2可使用双指针分别获取,h则可使用二分法遍历减少耗时。importjava.util.*;publicclassMain{privatestaticintN,L;priva......
  • 空指针、野指针和const修饰指针的区别
    关键:空指针和野指针都不允许访问,否则会报错。空指针指向变量中内存编号为0的空间用途1.初始化指针变量,并且空指针指向的内存不能进行解引用intmain(){ //指针变量p指向内存地址编号为0的空间 int*p=NULL; //访问空指针报错 //内存编号0~255为系统占用内......
  • c语言(1.指针、2.回调函数)实现简易计算器
    一级标题:简易计算器的实现。二级标题:代码中运用的知识在这里我会使用函数数组指针与回调函数分别写一份简易计算器的代码;那么函数数组指针与回调函数是什么呢?1、函数数组指针,形如intint(p[5])(int,int)的代码就是函数数组指针,p就是函数名,函数类型为int([])(int,int),其......
  • 工业智能物联网关如何助力工业防震减灾
    地震灾害难以预料,一旦发生往往就损失重大。对于工业领域而言,地震灾害的影响不仅仅是对人员安全的威胁,还包括对生产设施的破坏、生产进程的中断以及伴生的持续性经济损失。 随着5G、大数据、物联网技术的发展,面向工业领域构建一个高效的防震减灾系统已成为可能,不仅能够降低地震......
  • 构建人工智能的工具 —— VXscan-R:数字孪生环境软件模块
    地址:https://www.creaform3d.com.cn/zh/ji-liang-jie-jue-fang/vxscan-rshu-zi-luan-sheng-huan-jing-ruan-jian-mo-kuaiVXscan-RPlan不仅仅是一个机器人编程软件,它还是一个完整的项目准备环境,而VXscan-RExecute则是一个用于执行工作的程序套件。......
  • 【触想智能】工业触摸一体机九大常见故障检测方法分享
    工业触摸一体机目前在社会生产中应用非常广泛,比如智能化的生产车间、城市智慧安防监控中心都经常用到工业触摸一体机。电子产品在使用中难免会出现一些故障,工业触摸一体机也不例外。那么我们在使用工业触摸一体机的时遇到问题怎么办呢?下面小编给大家介绍几个常见故障检......
  • 学习人工智能:Attention Is All You Need-3-训练;结果;结论;Transformer模型相当于 E=MC^2
    5训练Training本节描述了我们模型的训练方案。5.1训练数据和批次处理TrainingDataandBatching我们在标准的WMT2014英德数据集上进行了训练,该数据集包含约450万个句子对。句子使用字节对编码[3]进行编码,其共享源-目标词汇表包含约37000个标记。对于英法翻译,我们使用......
  • 智能教育机器人、学生竞赛用机器人、创客教育用机器人 —— 未来大有可为 —— 市场空
    官方地址:https://www.lejurobot.com/talos-cn/淘宝:https://lejuznsb.tmall.com/shop/view_shop.htm?spm=pc_detail.27183998/evo365560b447259.202202.1.28477dd6PzEMYe看到说第一款国内操作系统平台的智能机器人,于是好奇的看了下产品介绍,乐聚机器人ROBAN,突然发现这个价格......
  • 讲解智能药箱系统的制作思路
    详细的智能药箱系统制作思路如下:设计硬件:a.药箱结构:设计一个合适大小和容量的药箱,保证可以容纳不同大小和形状的药品,并具备密封性能以保持药品的新鲜度。b.传感器:在药箱内部安装传感器,用于检测药品的存储情况,如温度传感器检测温度、湿度传感器检测湿度、光传感器检测光照......