C++速览之智能指针

1、存在的问题
c++ 把内存的控制权对程序员开放,让程序显式的控制内存,这样能够快速的定位到占用的内存,完成释放的工作。但是此举经常会引发一些问题,比如忘记释放内存。由于内存没有得到及时的回收、重复利用,所以在一些c++程序中,常会遇到程序突然退出、占用内存越来越多,最后不得不选择重启来恢复。造成这些现象的原因可以归纳为下面几种情况.

1.1 野指针
出现野指针的有几个地方 :
指针声明而未初始化,此时指针的将会随机指向
内存已经被释放、但指针仍然指向它。这时内存有可能被系统重新分配给程序使用,从而会导致无法估计的错误;

#include <iostream>using namespace std;int mian(){//1. 声明未初始化int *p1 ;cout << "打印p1: " << *p1 << endl;//2. 内存释放后,并没有置空 nullptrint *p = new int(55);cout << "释放前打印 :  " << *p << endl;delete  p ;cout << "释放后打印 :  " << *p << endl;return 0 ;
}

1.2 重复释放
程序试图释放已经释放过的内存,或者释放已经被重新分配过的内存,就会导致重复释放错误.

int main(){int *p = new int(4);//重复释放delete p;delete p;return 0 ;
}

1.3 内存泄漏
不再使用的内存,并没有释放,或者忘记释放,导致内存没有得到回收利用。 忘记调用delete

int main(){int *p = new int(4);//后面忘记调用delete p;return 0 ;
}

2、智能指针
为了解决普通指针的隐患问题,c++在98版本开始追加了智能指针的概念,并在后续的11版本中得到了提升
c++11标准用 unique_ptr | shared_ptr | weak_ptr 等指针来自动回收堆中分配的内存。智能指针的用法和原始指针用法一样,只是它多了些释放回收的机制罢了。

智能指针是 C++ 中为了避免内存泄漏等问题而引入的一种对象,它们是模板类,其行为类似于指针,但会自动管理所指向对象的生命周期。它们在构造时获取资源,在析构时释放资源,从而避免了手动调用delete操作符的需要

2.1 unique_ptr
unique_ptr 是一个独享所有权的智能指针,它提供了严格意义上的所有权
也就是只有这个指针能够访问这片空间,不允许拷贝,但是允许移动(转让所有权)

#include<iostream>
#include <memory>using namespace std;int main() {// 1. 创建unique_ptr对象,包装一个int类型指针unique_ptr<int> p(new int(10));// 2. 无法进行拷贝。编译错误//unique_ptr<int> p2 = p;cout << "-----1----" << *p << endl;// 3. 可以移动指针到p3. 则p不再拥有指针的控制权 p3 现在是唯一指针unique_ptr<int> p3 = move(p);cout << "-----2----" << *p3 << endl;// p 现在已经无法取值了。// cout << "-----3----" << *p << endl;// 可以使用reset显式释放内存。p3.reset();// 重新绑定新的指针p3.reset(new int(6));// 获取到包装的int类型指针int *p4 = p3.get();// 输出6cout << "-----4----" << "指针指向的值是:" << *p4 << endl;cout << "-----5----" << "*p3值是:" << *p3 << endl;return 0;
}

2.2 shared_ptr
shared_ptr : 允许多个智能指针共享同一块内存,由于并不是唯一指针,所以为了保证最后的释放回收,采用了计数处理,每一次的指向计数 + 1 , 每一次的reset会导致计数 -1 ,直到最终为0 ,内存才会最终被释放掉。 可以使用use_cout 来查看目前的指针个数

#include <iostream>
#include <memory>using namespace std;class Stu {
public:Stu() {cout << "执行构造函数" << endl;}~Stu() {cout << "执行析构函数" << endl;}
};int main() {shared_ptr<Stu> s1(new Stu());cout << " ---1--- = " << s1.use_count() << endl;  // 查看指向计数shared_ptr<Stu> s2 = s1;cout << " ---2--- = " << s1.use_count() << endl;  // 查看指向计数shared_ptr<Stu> s3 = s1;cout << " ---3--- = " << s1.use_count() << endl;  // 查看指向计数s1.reset();cout << " ---4--- = " << s3.use_count() << endl;  // 查看指向计数s2.reset();cout << " ---5--- = " << s3.use_count() << endl;  // 查看指向计数s3.reset();  // 至此全部解除指向 计数为0 。 会执行stu的析构函数cout << " ---6--- = " << s3.use_count() << endl;  // 查看指向计数return 0;
}

在这里插入图片描述
问题
对于引用计数法实现的计数,总是避免不了循环引用(或环形引用)的问题,即我中有你,你中有我,shared_ptr 也不例外。 下面的例子就是,这是因为 f 和 s 内部的智能指针互相指向了对方,导致自己的引用计数一直为 1,所以没有进行析构,这就造成了内存泄漏

#include <iostream>
#include <memory>using namespace std;class Son;class Father {
private:shared_ptr<Son> son;
public:Father() {cout << "father 构造" << endl;}~Father() {cout << "father 析构" << endl;}void setSon(shared_ptr<Son> son) {this->son = son;}
};class Son {
private:shared_ptr<Father> father;
public:Son() {cout << "son 构造" << endl;}~Son() {cout << "son 析构" << endl;}void setFather(shared_ptr<Father> father) {this->father = father;}
};int main() {shared_ptr<Father> f(new Father());shared_ptr<Son> s(new Son());f->setSon(s);s->setFather(f);
}

在这里插入图片描述
解决办法
定义对象的时候 ,用 shared_ptr 指针;引用对象的地方,用 weak_ptr 指针

2.3 weak_ptr

#include <iostream>
#include <memory>using namespace std;class Son;class Father {
private:weak_ptr<Son> son;
public:Father() {cout << "father 构造" << endl;}~Father() {cout << "father 析构" << endl;}void setSon(shared_ptr<Son> son) {this->son = son;}
};class Son {
private:weak_ptr<Father> father;
public:Son() {cout << "son 构造" << endl;}~Son() {cout << "son 析构" << endl;}void setFather(shared_ptr<Father> father) {this->father = father;}
};int main() {shared_ptr<Father> f(new Father());shared_ptr<Son> s(new Son());f->setSon(s);s->setFather(f);
}

在这里插入图片描述

PS:

shared_ptr:
允许多个shared_ptr指向同一个对象,使用引用计数来管理对象的生命周期,当最后一个 shared_ptr 被销毁或不再指向该对象时,对象才会被删除

#include <iostream>
#include <memory>int main() {std::shared_ptr<int> ptr1 = std::make_shared<int>(10);std::shared_ptr<int> ptr2 = ptr1; std::cout << *ptr1 << " " << *ptr2 << std::endl; return 0;
}

std::make_shared(10):创建一个shared_ptr并将其初始化为指向一个值为 10 的int对象;
std::shared_ptr ptr2 = ptr1;:ptr2和ptr1都指向同一个对象,它们会共享一个引用计数,当其中一个 shared_ptr 被销毁时,引用计数会减一,只有当引用计数为零时,对象才会被删除。

weak_ptr:
它是一种辅助 shared_ptr 的智能指针,不会影响所指向对象的生命周期。
主要用于解决 shared_ptr 的循环引用问题,因为循环引用会导致引用计数永远不为零,从而导致内存泄漏。

#include <iostream>
#include <memory>struct Node {std::shared_ptr<Node> next;~Node() { std::cout << "Node 被销毁" << std::endl; }
};int main() {std::shared_ptr<Node> node1 = std::make_shared<Node>();std::shared_ptr<Node> node2 = std::make_shared<Node>();node1->next = node2;node2->next = node1;// 存在循环引用,导致 Node 不会被销毁return 0;
}

为了解决上述问题,可以使用weak_ptr

#include <iostream>
#include <memory>struct Node {std::weak_ptr<Node> next;~Node() { std::cout << "Node 被销毁" << std::endl; }
};int main() {std::shared_ptr<Node> node1 = std::make_shared<Node>();std::shared_ptr<Node> node2 = std::make_shared<Node>();node1->next = node2;node2->next = node1;return 0;
}

当使用 std::shared_ptr 形成循环引用时,如第一个代码片段中,node1 和 node2 相互引用,它们的引用计数永远不会为零,即使它们超出了作用域,对象也不会被销毁。

在第二个代码片段中,使用 std::weak_ptr 替换 std::shared_ptr,weak_ptr 不会增加引用计数,当 node1和 node2 超出作用域时,它们会被正确销毁。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Pytorch实用教程】TCN(Temporal Convolutional Network,时序卷积网络)简介

文章目录 TCN的基本特点TCN的优点TCN的应用场景典型的TCN架构总结TCN(Temporal Convolutional Network,时序卷积网络)是一种用于处理序列数据的深度学习模型,尤其适用于时间序列预测、语音识别、自然语言处理等任务。它利用卷积神经网络(CNN)来处理时序数据,相比于传统的…

对话 TDengine 解决方案中心总经理陈肃:构建技术与市场的桥梁

TD 小T导读 他是大数据领域的杰出专家&#xff0c;拥有超过十项一作发明专利&#xff0c;是中国通信行业标准《大数据 消息中间件技术要求与测试方法》的重要编写者&#xff0c;并凭借数据中间件领域的突出成就荣获 2019 年“CJK OSS Award”。他是腾讯云 TVP 专家和 TGO 鲲鹏会…

【PCL】Segmentation 模块—— 欧几里得聚类提取(Euclidean Cluster Extraction)

1、简介 PCL 的 Euclidean Cluster Extraction&#xff08;欧几里得聚类提取&#xff09; 是一种基于欧几里得距离的点云聚类算法。它的目标是将点云数据分割成多个独立的簇&#xff08;clusters&#xff09;&#xff0c;每个簇代表一个独立的物体或结构。该算法通过计算点与点…

动态主机配置协议 (DHCPv4)介绍,详细DHCP协议学习笔记

定义 动态主机配置协议 (DHCP) 是一种用于集中对用户 IPv4 地址进行动态管理和配置的技术。为与 IPv6 动态主机配置协议 (DHCPv6) 进行区分&#xff0c;本文统一将动态主机配置协议称为 DHCPv4。 DHCPv4 协议由 RFC 2131 定义&#xff0c;采用客户端/服务器通信模式&#xff…

玩转大语言模型——使用graphRAG+Ollama构建知识图谱

系列文章目录 玩转大语言模型——ollama导入huggingface下载的模型 玩转大语言模型——langchain调用ollama视觉多模态语言模型 玩转大语言模型——使用graphRAGOllama构建知识图谱 文章目录 系列文章目录前言下载和安装用下载项目的方式下载并安装用pip方式下载并安装 生成知…

Nginx三种不同类型的虚拟主机(基于域名、IP 和端口)

&#x1f3e1;作者主页&#xff1a;点击&#xff01; Nginx-从零开始的服务器之旅专栏&#xff1a;点击&#xff01; &#x1f427;Linux高级管理防护和群集专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2025年1月15日13点14分 目录 1. 基于域名的虚拟主机 …

pytest-instafail:让测试失败信息即时反馈

pytest-instafail&#xff1a;让测试失败信息即时反馈 前言一、简介二、优势三、安装与使用3.1 未安装时运行情况3.2 安装3.3 已安装时运行情况3.3 pytest.ini 配置选项 四、对比 总结 前言 当测试用例数量庞大时&#xff0c;定位测试失败的原因往往耗时费力。此时&#xff0c;…

从 0 开始实现一个 SpringBoot + Vue 项目

从 0 开始实现一个 SpringBoot Vue 项目 从 0 开始实现一个 SpringBoot Vue 项目 软件和工具创建 SpringBoot 后端项目创建 MySQL 数据库配置文件实现增删改查接口 Model 层mapper 层service 层controller 层测试 实现项目功能接口 代码测试 创建 Vue 前端 安装 Node.js配置…

5. 推荐算法的最基础和最直观的认识

1.性别年龄转换为统一的计量单位 所谓推荐&#xff0c;就是替别人推荐&#xff0c;比如工厂A需要招男员工&#xff0c;希望大家推荐认识的人。那么在这里&#xff0c;就有了推荐的概念&#xff0c;限定条件是男。我们知道&#xff0c;人的性别一般分为男或者女。在这里假设把男…

ASP.NET Core - 配置系统之配置添加

ASP.NET Core - 配置系统之配置添加 2. 配置添加 2. 配置添加 配置系统可以读取到配置文件中的信息&#xff0c;那必然有某个地方可以将配置文件添加到配置系统中。之前的文章中讲到 ASP.NET Core 入口文件中&#xff0c;builder(WebApplicationBuilder 对象) 中有一个 Config…

AI编程工具横向评测--Cloudstudio塑造完全态的jupyter notebook助力数据分析应用开发

AI编程工具横向评测–Cloudstudio塑造完全态的jupyter notebook助力数据分析应用开发 数据分析类应用的开发&#xff0c;指的是首先进行数据分析&#xff0c;比如统计学分析、机器学习模型的构建等&#xff0c;然后将分析的流程开发成数据分析类的工具&#xff0c;或者将数据分…

HBase实训:纸币冠字号查询任务

一、实验目的 1. 理解分布式数据存储系统HBase的架构和工作原理。 2. 掌握HBase表的设计原则&#xff0c;能够根据实际业务需求设计合理的表结构。 3. 学习使用HBase Java API进行数据的插入、查询和管理。 4. 实践分布式数据存储系统在大数据环境下的应用&#xff0c;…

算法与数据结构——复杂度

目录 一 数据结构前言 1 数据结构 2 算法 3 算法与数据结构的关系 二 算法效率 1 算法效率&#xff1a; 2 复杂度 2.1 概念&#xff1a; 2.2分类&#xff1a; 2.3 空间复杂度在计算机高速发展的现代重要吗&#xff1f; 3 复杂度的重要性 三 时间复杂度…

usb通过hdc连接鸿蒙next的常用指令

参考官方 注册报名https://www.hiascend.com/developer/activities/details/44de441ef599450596131c8cb52f7f8c/signup?channelCodeS1&recommended496144 hdc-调试命令-调测调优-系统 - 华为HarmonyOS开发者https://developer.huawei.com/consumer/cn/doc/harmonyos-guid…

论文笔记-arXiv2025-A survey about Cold Start Recommendation

论文笔记-arXiv2025-Cold-Start Recommendation towards the Era of Large Language Models: A Comprehensive Survey and Roadmap 面向大语言模型&#xff08;LLMs&#xff09;时代的冷启动推荐&#xff1a;全面调研与路线图1.引言2.前言3.内容特征3.1数据不完整学习3.1.1鲁棒…

如何将数据库字符集改为中文,让今后所有的数据库都支持中文

最后一行有我自己的my.ini文件 数据库输入中文数据时会变为乱码&#xff0c; 这个时候&#xff0c;我们为每个数据库设置字符集&#xff0c;太过于麻烦&#xff0c;为数据库单独设置重启后又会消失 Set character_set_database’utf8’; Set character_set_server’utf8’; …

AI刷题-小R的随机播放顺序、不同整数的计数问题

目录 一、小R的随机播放顺序 问题描述 测试样例 解题思路&#xff1a; 问题理解 数据结构选择 算法步骤 最终代码&#xff1a; 运行结果&#xff1a; 二、 不同整数的计数问题 问题描述 测试样例 解题思路&#xff1a; 问题理解 数据结构选择 算法步骤 最终…

从零搭建SpringBoot3+Vue3前后端分离项目基座,中小项目可用

文章目录 1. 后端项目搭建 1.1 环境准备1.2 数据表准备1.3 SpringBoot3项目创建1.4 MySql环境整合&#xff0c;使用druid连接池1.5 整合mybatis-plus 1.5.1 引入mybatis-plus1.5.2 配置代码生成器1.5.3 配置分页插件 1.6 整合swagger3&#xff08;knife4j&#xff09; 1.6.1 整…

在.NET用C#将Word文档转换为HTML格式

将Word文档转换为HTML格式尤其具有显著的优势&#xff0c;它不仅能够确保文档内容在多种设备和平台上保持一致灵活的显示&#xff0c;还便于通过网络进行传播和集成到各种Web应用中。随着越来越多的企业和开发者寻求更灵活、更具兼容性的文件处理方式&#xff0c;.NET框架下的C…