智能指针——C++

news2024/11/23 4:09:25

智能指针相较于普通指针的区别,就是智能指针可以不用主动释放内存空间,系统会自动释放,避免了内存泄漏。

1、unique_ptr:独占指针

需包含的头文件:#include <memory>

unique_ptr 三种定义方式

先定义一个类

class cat
{
	cat(std::string name);
	~cat();
	void cat_info() const //输出一下成员name的值
	{
		std::cout<<"name:"<<this->name<<std::endl;
	}
	void set_cat_name(const std::string &name) //修改成员name的值
	{
	this->name=name;
	}
	std::string get_name() const
	{
		return name;
	}
private:
	std::string name{"mimi"};
};
  1. 使用std::make_unique的方式:推荐使用的方式
	//方法一 std::make_unique
	std::unique_ptr<cat> u_c_p4 = std::make_unique<cat>("ee");
	std::string a=u_c_p4->get_name();
	std::cout << "a:" << a << std::endl;

在这里插入图片描述

  1. 使用new的方式声明智能指针
	//方法二 new
	std::unique_ptr<cat> u_c_p3{ new cat("cc")};

	std::cout << "-------------" << std::endl;

在这里插入图片描述
我们只new了这个类对象,并没有delete释放他的地址,但是智能指针会在程序执行结束自动调用这个类的析构函数。

  1. 利用普通指针声明
	cat *c_p2 = new cat("xy"); //声明一个普通指针
	std::unique_ptr<cat> u_c_p2{ c_p2 }; // 声明一个智能指针指向普通指针的地址
	
	//正能指针和普通指针的指向的地址相同,两个指针任意一个改变,对方也会跟着改变
	c_p2->cat_info();
	u_c_p2->cat_info();
	c_p2->set_cat_name("aa");
	c_p2->cat_info();
	u_c_p2->cat_info();
	u_c_p2->set_cat_name("bb");
	c_p2->cat_info();
	u_c_p2->cat_info();
	std::cout << "-------------" << std::endl;
	return 0;

在这里插入图片描述

用该方法声明智能指针有一个确定,普通指针依然可以使用,如果普通指针改变地址的值,那么智能指针的值也会跟着改变。

unique_ptr 做函数参数

  • unique_ptr不能被复制,只可以move,即当要让unique_ptr 的变量做函数参数,直接将变量放在括号里是不可以的,可以用move函数
void do_unique_value(std::unique_ptr<cat> c)
{
	c->cat_info();
}
int main()
{
	//方法一 std::make_unique
	std::unique_ptr<cat> u_c_p4 = std::make_unique<cat>("ee");
	do_unique_value(u_c_p4);   //直接赋值到括号里是不行的
	u_c_p4->cat_info(); // 在move之前是可以调用的,但是move之后是不能调用的
	do_unique_value(std::move(u_c_p4));  //要用move函数进行值传递
	
	u_c_p4->cat_info(); //这里就能调用了
	//但是在move之后这个变量是不能调用的

	do_unique_value(std::make_unique<cat>()); // 直接构造

	std::cout << "-------------" << std::endl;
	return 0;
}

在这里插入图片描述

如果值是引用的话,可以不用move

void do_unique_value2(std::unique_ptr<cat> &c)
{
	c->cat_info();
}
	do_unique_value(u_c_p4);  //参数是引用,就可以直接传值
	u_c_p4->cat_info();  //并且之后也可以在使用该变量

2、shared_ptr 计数指针、共享指针

shared_ptr基础概念

  • shared_ptr区别于unique_ptr的区别是shared_ptr可以被复制,shared_ptr创建了一个计数器对象,与类对象所指向的内存地址关联。
  • copy一次shared_ptr的计数器就加1,销毁一次计数器就减一
  • 调用shared_ptr计数器的API是use_count()
  1. shared_ptr指针变量的赋值后,use_count()计数值就为1,然后将shared_ptr 变量赋给其他值,本身和另一个值的value值相同,计数值都为2,是在原本shared_ptr值为1的基础上+1。
  2. 如果两个以上存储着相同地址的shared_ptr值,其中一个给赋值为nullptr,的话,那么只有被赋值为空的变量值为空,其余几个value值不变,但是计数值都会在原有基础上-1。
	std::shared_ptr<int> i_p_1 = std::make_shared<int>(10);
	std::cout << "value:" << *i_p_1 << std::endl;
	// 第一次赋值计数为1
	std::cout << "use count:" << i_p_1.use_count() << std::endl; 
	std::cout << "-------------" << std::endl;
	//结果
	// value:10
	// use count : 1
	
	//copy
	std::shared_ptr<int> i_p_2 = i_p_1;
	std::cout << "i_p_1 value:" << *i_p_1 << std::endl;
	std::cout << "i_p_2 value:" << *i_p_2 << std::endl;
	//将i_p_1的值给i_p_2,对该地址有一次进行复制操作,计数+1,i_p_1和i_p_2代表的地址相同,存储值和计数值都相同
	std::cout << "i_p_1 use count:" << i_p_1.use_count() << std::endl;
	std::cout << "i_p_2 use count:" << i_p_2.use_count() << std::endl;
	std::cout << "-------------" << std::endl;
	//结果:
	//value:10
    //value:10
    //use count : 2
    //use count : 2

	//修改值
	*i_p_2 = 30;
	std::cout << "i_p_1 value:" << *i_p_1 << std::endl;
	std::cout << "i_p_2 value:" << *i_p_2 << std::endl;
	std::cout << "i_p_1 use count:" << i_p_1.use_count() << std::endl;
	std::cout << "i_p_2 use count:" << i_p_2.use_count() << std::endl;
	std::cout << "-------------" << std::endl;
	//结果
	//i_p_1 value : 30
	//i_p_2 value : 30
	//i_p_1 use count : 2
	//i_p_2 use count : 2
	
	// 三个值表示同一个地址,将其中一个赋值为nullptr
	std::shared_ptr<int> i_p_3 = i_p_1;
	std::cout << "---------i_p_1 = nullptr befo---------" << std::endl;
	std::cout << "i_p_1 value:" << *i_p_1 << std::endl;
	std::cout << "i_p_2 value:" << *i_p_2 << std::endl;
	std::cout << "i_p_3 value:" << *i_p_3 << std::endl;
	// 将i_p_1赋值给i_p_3,计数值在2的基础上+1=3
	std::cout << "i_p_1 use count:" << i_p_1.use_count() << std::endl;
	std::cout << "i_p_2 use count:" << i_p_2.use_count() << std::endl;
	std::cout << "i_p_2 use count:" << i_p_3.use_count() << std::endl;
	i_p_1 = nullptr;
	std::cout << "------i_p_1 = nullptr after-----------"<<std::endl;
	//std::cout << "i_p_1 value:" << *i_p_1 << std::endl; //赋空之后不能输出
	std::cout << "i_p_2 value:" << *i_p_2 << std::endl;
	std::cout << "i_p_3 value:" << *i_p_3 << std::endl;
	//i_p_1赋值为空后,i_p_1的计数值直接为0,但是i_p_2和i_p_3的计数值会在原来3的基础上-1
	std::cout << "i_p_1 use count:" << i_p_1.use_count() << std::endl;
	std::cout << "i_p_2 use count:" << i_p_2.use_count() << std::endl;
	std::cout << "i_p_2 use count:" << i_p_3.use_count() << std::endl;
	// 结果
	// ---------i_p_1 = nullptr befo---------
	//i_p_1 value : 30
	//i_p_2 value : 30
	//i_p_3 value : 30
	//i_p_1 use count : 3
	//i_p_2 use count : 3
	//i_p_2 use count : 3
	//------i_p_1 = nullptr after-----------
	//i_p_2 value : 30
	//i_p_3 value : 30
	//i_p_1 use count : 0
	//i_p_2 use count : 2
	//i_p_2 use count : 2

shared_ptr函数

shared_ptr变量是可以copy,可以被当做值直接复制给函数,传入函数体后,在函数体内部计数值依然会进行加减操作,但操作尽在函数体内有效,出了函数体,计数值会恢复到原来的数值。但是在函数体里面对指针的值进行修改的话,该值会被修改。

void cat_by_value(std::shared_ptr<cat> cat)
{
	cat->set_cat_name("ee");
	cat->cat_info();
	std::cout << "use count:" << cat.use_count() << std::endl;
}

	std::shared_ptr<cat> c1 = std::make_shared<cat>("dd");
	c1->cat_info();
	std::cout << "c1 use count:" << c1.use_count() << std::endl;
	std::cout << "-------------" << std::endl;

	cat_by_value(c1);
	std::cout << "-------------" << std::endl;

	c1->cat_info();
	std::cout << "c1 use count:" << c1.use_count() << std::endl;
	//结果
	//cat:dd
	//name:dd
	//c1 use count : 1
	//------------ -
	//name : ee
	//use count : 2
	//------------ -
	//name : ee
	//c1 use count : 1
	//del : ee

3、shared_ptr与unique_ptr

  • 不能将shared_ptr转化为unique_ptr
  • 可以将unique_ptr转化为shared_ptr,使用std::move

如果函数有返回值的话,返回值最好设计为unique_ptr,的类型,应为unique_ptr既可以赋值给shared_ptr,也可以赋值给shared_ptr

  • 可以用move函数将unique_ptr的值赋给shared_ptr类型
  • 也可以将函数返回的unique_ptr类型的数据直接赋值给shared_ptr类型
std::unique_ptr<cat> get_unique_ptr()
{
	std::unique_ptr<cat> cat_p = std::make_unique<cat>("local cat");
	return cat_p;
}

int main()
{
	std::unique_ptr<cat> c_p_1 = std::make_unique<cat>("dd");
	// 使用move的方式赋值
	std::shared_ptr<cat> c_p_2 = std::move(c_p_1);
	std::cout << "use count:" << c_p_2.use_count() << std::endl;
	c_p_2->get_name();
	std::cout << "use count:" << c_p_2.use_count() << std::endl;
	std::cout << "-------------" << std::endl;
	// 函数返回值赋值
	std::shared_ptr<cat> c_p_3 = get_unique_ptr();
	c_p_3->get_name();
	std::cout << "use count:" << c_p_3.use_count() << std::endl;
	std::cout << "-------------" << std::endl;
	
	return 0;
}

weak_ptr

  • weak_ptr没有内存的所有权,所以不能调用->和解引用*
  • weak_ptr可以通过lock()提升为shared_ptr类型

weak_ptr不能单独存在,一般需要借助shared_ptr,来声明

  • 将shared_ptr的值给w_p_1对象,是可以的,并且w_p_1可以调用use_count()计数值,但是在飞w_p_1对象赋值的过程中shared_ptr这个对象的计数值不会+1
  • 使用lock(),将weak_ptr的值赋值给shared_ptr,两者的use_count()计数值都会相加。
	std::shared_ptr<cat> s_p_1 = std::make_shared<cat>("c1");
	//将shared_ptr的值给w_p_1对象,是可以的,并且w_p_1可以调用use_count()计数值,但是在飞w_p_1对象赋值的过程中shared_ptr这个对象的计数值不会+1
	std::weak_ptr<cat> w_p_1(s_p_1);

	std::cout << "s_p_1 count:" << s_p_1.use_count() << std::endl;
	std::cout << "w_p_1 count:" << w_p_1.use_count() << std::endl;
	std::cout << "-------------" << std::endl;
	std::shared_ptr<cat> s_p_2 = w_p_1.lock();
	std::cout << "s_p_1 count:" << s_p_1.use_count() << std::endl;
	std::cout << "w_p_1 count:" << w_p_1.use_count() << std::endl;
	std::cout << "s_p_1 count:" << s_p_2.use_count() << std::endl;
//cat:c1  //构造函数
//s_p_1 count:1
//w_p_1 count:1
//-------------
//s_p_1 count:2
//w_p_1 count:2
//s_p_1 count:2
//del:c1  //析构函数

循环依赖的问题

class cat
{
public:
	cat();
	cat(std::string name);
	~cat();
	void set_frient(std::shared_ptr<cat> c)
	{
		m_friend = c;
	}
private:
	std::string name{ "mimi" };
	std::shared_ptr<cat> m_friend;  
};
	std::shared_ptr<cat> c3 = std::make_shared<cat>("c3");
	std::shared_ptr<cat> c4 = std::make_shared<cat>("c4");
	//运行结果
	//cat:c3		//构造
	//cat:c4 	//构造
	//del:c4		//析构
	//del:c3		//析构

	如果在c3和c4的类中都定义一个cat类型的变量存储着对方,那么两者就存在着依赖
	std::shared_ptr<cat> c3 = std::make_shared<cat>("c3");
	std::shared_ptr<cat> c4 = std::make_shared<cat>("c4");
	c3->set_frient(c4);
	c4->set_frient(c3);
	//运行结果,只够构造、没有析构
	// cat:c3
	// cat:c4
解决方式只需将类定义时的shared_ptr类型的对象改为weak_ptr
class cat
{
public:
	cat();
	cat(std::string name);
	~cat();
	void set_frient(std::shared_ptr<cat> c)
	{
		m_friend = c;
	}
private:
	std::string name{ "mimi" };
	std::weak_ptr<cat> m_friend; //这需要把这里修改为weak_ptr类型
};
	// 然后运行相同的代码
	std::shared_ptr<cat> c3 = std::make_shared<cat>("c3");
	std::shared_ptr<cat> c4 = std::make_shared<cat>("c4");
	c3->set_frient(c4);
	c4->set_frient(c3);
 //运行结果
 //cat:c3
 //cat:c4
 //del:c4
 //del:c3

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/449314.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

learn_C_deep_5 (温故知新、sigend char a = -128的深度理解、unsigned int类型的写法规范)

目录 温故知新 理解"unsigned int a -10;" 如何理解大小端 大小端的概念 大小端是如何影响数据存储的 sigend char a -128的深度理解 10000000为什么是-128&#xff0c;而不是-0 代码练习 unsigned int类型的写法规范 温故知新 理解"unsigned int a…

python数据结构与算法-动态规划(最长公共子序列)

一、最长公共子序列问题 1、问题概念 一个序列的子序列是在该序列中删去若干元素后得 到的序列。 例如&#xff1a;"ABCD”和“BDF”都是“ABCDEFG”的子序列。 最长公共子序列(LCS) 问题: 给定两个序列X和Y&#xff0c;求X和Y长度最大的公共子字列。 例:X"ABBCBDE”…

【ABAQUS Python二次开发】 debug : ini解析ERROR:没有实例属性‘__getintem__’

我的主页&#xff1a; 技术邻&#xff1a;小铭的ABAQUS学习的技术邻主页博客园 : HF_SO4的主页哔哩哔哩&#xff1a;小铭的ABAQUS学习的个人空间csdn&#xff1a;qgm1702 博客园文章链接&#xff1a; https://www.cnblogs.com/aksoam/p/17287136.html abaqus python 搭配ini…

古埃及:金字塔

文章目录 I 建造金字塔1.1 切割巨石1.2 开凿巨石1.3 摞石1.4 大金字塔的入口呈三角形 see also I 建造金字塔 在生活中&#xff0c;事实是正确的&#xff0c;如果理论解释不了现实&#xff0c;需要更正理论。 1.1 切割巨石 建筑材料巨石的切割&#xff1a;把石英砂粘在了铜锯…

记一次Macbook pro电池修复

记一次Macbook pro电池修复 mac版本 A1708 问题描述 Macbook更换新电池后&#xff0c;在项头栏中&#xff0c;没有显示电池图标&#xff0c;系统设置里面也找不到电池图标。这样开机还得连着电源线 ~ ^~ 原因分析&#xff1a; 有可能是电池排线坏了。 解决方案&#xff1a…

【C/C++】C++11 线程库重大历史意义

文章目录 C11 线程库重大意义【C11 中最重要的特性&#xff1a;就是对线程进行支持】API 比较C11 线程库APILinux/Win 系统线程库 API代码示例 Demo C11 线程库重大意义【C11 中最重要的特性&#xff1a;就是对线程进行支持】 C11 线程库解决了历史多线程跨平台问题&#xff0…

C++语法(20)---- 模拟红黑树

C语法&#xff08;19&#xff09;---- 模拟AVL树_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/130229501?spm1001.2014.3001.5501 目录 1.红黑树介绍 2.模拟实现 1.枚举红黑颜色 2.节点的定义 3.树类框架 4.插入 5.检查 3.代码实现 1…

【开发经验】spring事件监听机制关心的同步、异步、事务问题

文章目录 spring发布订阅示例同步核心源码分析如何配置异步事务问题 观察者模式又称为发布订阅模式&#xff0c;定义为&#xff1a;对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖它的对象都得到通知并被自动更新。 如下图所示&…

【Go】六、并发编程

文章目录 并发编程1、并发介绍2、Goroutine3、runtime包 3、Channel3.1、channel相关信息 4、Goroutine池&#xff08;❌&#xff09;5、定时器6、select多路复用7、并发安全和锁8、Sync9、原子操作&#xff08;atomic包&#xff09; 并发编程 1、并发介绍 1、进程和线程 ​…

心塞,被面试官在朋友圈吐槽了

​前阵子一个后辈小学弟向我诉苦&#xff0c;说自己在参加某大厂测试的时候被面试官怼得哑口无言&#xff0c;场面让他一度十分尴尬。 印象最深的就是下面几个问题&#xff1a; 自动化测试中&#xff0c;如何解决Case依赖&#xff1f;你们公司业务中&#xff0c;自动化和手工分…

“五一”预订量创5年新高!如何制定营销活动引爆门店客流?

作为疫情3年经济复苏后&#xff0c;2023年的第一个长假&#xff0c;今年“五一”的消费需求将全面集中释放&#xff0c;带动全国各地线下实体生意全面复苏。 根据官方平台发布的数据显示&#xff0c;今年五一的旅游订单比疫情前的2019年增长了200%&#xff0c;是近5年预订量最多…

Spring Security 整体架构

Spring Security 整体架构 整体架构 在的架构设计中&#xff0c;认证 和 授权 是分开的&#xff0c;无论使用什么样的认证方式。都不会影响授权&#xff0c;这是两个独立的存在&#xff0c;这种独立带来的好处之一&#xff0c;就是可以非常方便地整合一些外部的解决方案。 认…

(数字图像处理MATLAB+Python)第五章图像增强-第三节:基于照度反射模型的图像增强

文章目录 一&#xff1a;基于同态滤波的增强&#xff08;1&#xff09;概述&#xff08;2&#xff09;程序 二&#xff1a;Retinex理论&#xff08;1&#xff09;Retinex理论概述&#xff08;1&#xff09;SSR&#xff08;单尺度Retinex 算法&#xff09;&#xff08;2&#xf…

Oracle的学习心得和知识总结(二十二)|Oracle数据库Real Application Testing之Database Replay实操(二)

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《Oracle Database SQL Language Reference》 2、参考书籍&#xff1a;《PostgreSQL中文手册》 3、EDB Postgres Advanced Server User Gui…

操作系统概述及Linux基本指令(1)

目录 一. 操作系统 1.1 什么是操作系统 1.2 操作系统的核心工作 二. Linux的基本指令 2.1 ls指令 -- 打印文件名 2.2 pwd指令 -- 显示路径 2.3 cd指令 -- 进入特定目录 2.4 touch指令 -- 创建普通文件 2.5 mkdir指令 -- 创建路径 2.6 rmdir/rm指令 -- 删除路径或普通…

【GeoDjango框架解析】空间方法的ORM查询

原文作者&#xff1a;我辈理想 版权声明&#xff1a;文章原创&#xff0c;转载时请务必加上原文超链接、作者信息和本声明。 Django数据操作-ORM 第一章 【Django开发入门】ORM的增删改查和批量操作 第二章 【Django开发入门】ORM查询分页以及返回Json格式 文章目录 Django数据…

中国人民大学与加拿大女王大学金融硕士——在职读研该如何平衡学习与工作呢

边工作边考研&#xff0c;对于所有人来说都是个不小的挑战&#xff0c;每年都有大量在职生因为焦躁、压力而中途离场。学习时间碎片化&#xff0c;复习进度特别容易被工作上的事情所打断&#xff0c;再想“重新启动”就会很难。想要节省备考时间建议你读免联考的中外合作办学项…

基于Canvas实现图片的上传和渲染

1 Canvas 1.1 什么是Canvas 是一个可以使用脚本 (通常为JavaScript) 来绘制图形的 HTML 元素。例如&#xff0c;它可以用于绘制图表、制作图片构图或者制作简单的动画。 1.2 基本使用 宽高&#xff1a; <canvas id"tutorial" width"150" height&qu…

<C++>C++入门

目录 前言 一、C关键字&#xff08;C98&#xff09; 二、命名空间 1. 命名空间定义 2. 命名空间的使用 3. 命名空间的使用有三种方式&#xff1a; 三、C输入&输出 四、缺省参数 1. 缺省参数概念 2. 缺省参数的分类 3. 缺省参数的应用 五、函数重载 六、引用 1. 引用的概念…

web实验(2)

&#xff08;1&#xff09; 应用html标签和css完成如下所示页面效果&#xff0c;图片见附件。 说明&#xff1a; 内容相对于浏览器居中,宽860px鼠标移动至列表项上&#xff0c;显示背景色#F8F8F8分割线2px solid #ccc&#xff0c;每项高130px第一行文字&#xff1a;20px 黑体…