【C++ 】智能指针:内存管理的 “自动导航仪”

【C++ 】智能指针:内存管理的 “自动导航仪”

目录

一、引入

二、智能指针的两大特性:

1、RAII

特点:

好处:

2、行为像指针

三、智能指针起初的缺陷:拷贝问题

四、几种智能指针的介绍。

1、C++98出现的智能指针——auto_ptr

auto_ptr解决上述拷贝构造的问题:

2、boost库

3、unique_ptr

4、shared_ptr

引用计数的实现:

赋值运算符的问题:(循环引用)

5、weak_ptr

特点:

解决循环引用问题:

五、C++智能指针的基本框架:

六、定制删除器,以及包装器的使用场景之一

七、内存泄漏:

1、什么是内存泄漏,内存泄漏的危害:

2、内存泄漏的分类

八、关于C++智能指针的相关代码:

std::unique_ptr

std::shared_ptr

std::weak_ptr


前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家

点击跳转到网站

一、引入

首先通过一个使用场景来引入智能指针,如下:

这里有一个类HF,一个子函数fun,在fun里面new了三个HF对象,然后delete,正常情况下delete会先调用析构函数,然后再释放相应的资源:

二、智能指针的两大特性:

智能指针的两大特性:

1、RAII

2、行为像指针

1、RAII

  是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。(通俗来讲,就是将资源交给一个类对象来管理,通过该类的构造函数交给对象。)

特点:

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。

好处:

(1)、不需要显式地释放资源,而是通过智能指针间接帮忙释放(2)、采用这种方式,对象所需的资源在其生命期内始终保持有效

2、行为像指针

智能指针实际也是一个类,要是行为像一个指针,即要重载解引用(*),箭头(->),甚至有时还要重载方括号([ ])。

三、智能指针起初的缺陷:拷贝问题

首先我们实现一个简易版智能指针:

new了一个日期类对象交给智能指针管理,智能指针对象存在期间,资源都是存在的,最后智能指针对象生命周期结束,调用析构函数释放,同时释放掉资源(delete);

但是当我们用对象sp1去拷贝构造sp2时:



此时就会报错:



原因在于我们没有实现拷贝构造,此时默认拷贝构造就是浅拷贝,这样两个对象的成员变量都会指向这份资源,最后调用析构函数时,就会对这份资源delete两次,从而造成野指针的问题。如何解决这个问题,在第四点进行介绍。

四、几种智能指针的介绍。

1、C++98出现的智能指针——auto_ptr

头文件:memory

具体信息可以查看官网文档。

auto_ptr解决上述拷贝构造的问题:

auto_ptr是直接将资源的管理权转移,用对象sp1去拷贝构造sp2,那么就会将sp1的资源的管理权交给sp2管理,而sp1被置空。



大致处理方法如下:拷贝后将sp1置空就行了:



注意,C++11的移动语义也是资源的转移,但和这里是不一样的,移动语义是针对将亡值去转移资源,而这里sp1不是将亡值。

这样做是有些问题的,这里资源转移后,sp1就悬空了,此时拷贝后就不能去访问sp1,否则就会出现空指针的问题,所以很多公司都禁止使用auto_ptr;

2、boost库

提到智能指针,就得提一下boost库,boost库是C++第三方库,里面就有智能指针,而C++的智能指针就是从这个库里面引入的,然后进行了略微修改。

3、unique_ptr

该智能指针解决拷贝构造的问题的方法就是:简单粗暴,禁止拷贝,适用于不需要拷贝的场景。



底层实际就是将拷贝构造给delete了:



同时,赋值运算符重载也要禁掉,默认生成的赋值运算符重载也是浅拷贝。

4、shared_ptr

当遇到需要拷贝构造的场景时,就需要使用shared_ptr,shared_ptr解决拷贝构造的问题的方法是:引用计数,去解决多次析构的问题。

引用计数的实现:

引用计数:记录当前有几个对象参与管理这个资源,在某个对象析构时,就将引用计数减1,当最后一个对象析构时才去释放资源。



要实现引用计数,就需要一份资源对应一个计数,有人会想到定义一个静态成员count,但实则不行,因为静态成员是属于整个类的,属于所有对象。管理一个资源的时候是可以解决的,但当第二个资源出现时,就不能适用了,因为不同资源之间的引用计数都是同一个静态成员变量,所以会相互影响。
实际上的实现如下:



增加一个成员变量*pcount,即指向引用计数的指针,在构造的时候(即资源来了),就new一个计数给该指针,在拷贝构造发生的时候,除了使两个对象指向同一个资源外,两个对象的引用计数也要指向同一个,并且要记得把引用计数++一下,在某个对象析构时,就将引用计数减1,然后判断是否为最后一个对象的析构,如果是的话就释放资源。

赋值运算符的问题:(循环引用)

shared_ptr虽然解决了拷贝构造的问题,但正因为引用计数的这样实现,又会造成赋值运算符重载后出现问题。

赋值运算符简单重载:



为了分析这里的缺陷,我们引入一个场景:双向链表的赋值:

这是双向链表的简单实现



因为会将链表资源交给智能指针管理,如下:



所以链表的定义中,成员next和prev的类型也应该是智能指针,不然在赋值的时候会出现类型不同的问题,正因为需要这样设计,问题就来了。

一般情况上述实现是没有问题的,但当执行下面两句代码后,问题就来了:



这是在链接两个节点,链接完后就会这样:



首先出现两个节点分别由n1和n2指向,此时两个节点的引用计数分别都是1,当执行n1->next = n2时,n2指向的节点的引用计数就会变成2;当执行n2->prve = n1时,n1指向的节点的引用计数就会变成2。

最后当析构链表时:



这样就形成了一个闭环,导致这两个节点的内存泄漏,这个问题也叫循环引用。当两个shared_ptr互相引用就会出现循环引用的问题。

5、weak_ptr

为了解决shared_ptr的循环引用问题,所以引入了weak_ptr。

特点:

weak_ptr的特点:没有引用计数,支持默认构造,构造函数的形参没有指针,因为该智能指针不参与资源管理,但自身成员变量会有一个指针,但会被置空,weak_ptr的重点在于拷贝构造和赋值。

解决循环引用问题:

这里的不同是将链表的成员变量_next和_prev的类型变为weak_ptr,正因为weak_ptr没有增加引用计数,所以在节点链接的时候,引用计数不会增加,所以节点会正常释放。



五、C++智能指针的基本框架:

六、定制删除器,以及包装器的使用场景之一

七、内存泄漏:

1、什么是内存泄漏,内存泄漏的危害:

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

2、内存泄漏的分类

C++中我们一般关心两种分类:(1)、堆内存泄漏(Heap leak)堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。(2)、系统资源泄漏指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

八、关于C++智能指针的相关代码:

此小点内容来源于:豆包AI

在 C++ 里,手动管理动态分配的内存容易引发内存泄漏、悬空指针等问题。智能指针作为一种类模板,能有效管理动态分配的内存,避免这些问题的出现。C++ 标准库提供了三种主要的智能指针:std::unique_ptrstd::shared_ptr 和 std::weak_ptrstd::unique_ptr

std::unique_ptr 属于独占式智能指针,它对所指向的对象拥有唯一的所有权。一旦 std::unique_ptr 被销毁,其指向的对象也会随之被自动销毁。 

在 uniquePtrExample 函数中: 借助 std::make_unique 创建了一个 std::unique_ptr,它指向 MyClass 的一个对象。调用 doSomething 方法来使用这个对象。尝试复制 std::unique_ptr 会引发编译错误,因为它不允许复制,不过可以使用 std::move 转移所有权。转移所有权之后,原 std::unique_ptr 变为空。std::shared_ptr

std::shared_ptr 是共享式智能指针,多个 std::shared_ptr 能够指向同一个对象。它采用引用计数来管理对象的生命周期,当引用计数变为 0 时,对象就会被销毁。 

在 sharedPtrExample 函数中: 利用 std::make_shared 创建了一个 std::shared_ptr,它指向 MyClass 的一个对象。通过 use_count 方法可以查看当前的引用计数。复制 std::shared_ptr 会使引用计数增加。调用 reset 方法可以释放 std::shared_ptr,从而使引用计数减少。std::weak_ptr

std::weak_ptr 是弱引用智能指针,它不拥有对象的所有权,只是对 std::shared_ptr 所管理的对象进行弱引用。std::weak_ptr 主要用于解决 std::shared_ptr 的循环引用问题。 

在 weakPtrExample 函数中: 定义了 A 和 B 两个类,其中 A 类包含一个 std::shared_ptr<B> 成员,B 类包含一个 std::weak_ptr<A> 成员。创建了 A 和 B 的 std::shared_ptr 对象,并相互引用。由于 B 类使用了 std::weak_ptr,所以不会出现循环引用,当 a 和 b 离开作用域时,对象能够被正确销毁。

Read more

OpenClaw 技能扩展实战指南:从安装 Skills 到 Tavily 联网 + 多维表格自动化

OpenClaw 技能扩展实战指南:从安装 Skills 到 Tavily 联网 + 多维表格自动化

OpenClaw 技能扩展实战指南:从安装 Skills 到 Tavily 联网 + 多维表格自动化 适用环境:macOS / Windows 核心工具:OpenClaw + ClawHub + Tavily API + 多维表格(如飞书多维表格) 一、OpenClaw 的 Skills 功能概述 OpenClaw 是一个高度可扩展的机器人智能体框架,其核心能力之一是 Skills(技能)系统。 Skills 以插件形式存在,赋予机器人执行特定任务的能力,分为两大类: 🔹 基础操作 Skills * 移动控制:路径规划、避障、自主导航 * 机械臂操作:抓取、放置、旋转等精细动作 * 传感器融合:整合激光雷达、摄像头、IMU 等数据 🔸 高级功能 Skills

By Ne0inhk
Python Selenium全栈指南:从自动化入门到企业级实战

Python Selenium全栈指南:从自动化入门到企业级实战

🌟 嗨,我是Lethehong!🌟 🌍 立志在坚不欲说,成功在久不在速🌍 🚀 欢迎关注:👍点赞⬆️留言收藏🚀 🍀欢迎使用:小智初学计算机网页AI🍀 目录 一、自动化测试的革命性工具 1.1 浏览器自动化的价值 1.2 Selenium生态全景图 二、环境搭建与基础配置 2.1 全平台安装指南 2.1.1 基础组件安装 2.1.2 浏览器驱动自动化配置  2.2 跨浏览器配置矩阵 三、核心操作全解析 3.1 元素定位的八种武器 3.1.1 基础定位器 3.1.2 XPath高级技巧 3.2 页面交互完全手册

By Ne0inhk
如何轻松避免网络负载过大

如何轻松避免网络负载过大

您是否在如何避免网站上出现过大的网络负载方面遇到问题? 如果您刚刚使用 PageSpeed Insights 或 Lighthouse 运行了您的网站,您可能会看到“避免巨大的网络负载”诊断消息,如果页面文件大小超过 1.6 MB,则会触发此消息。 幸运的是,有一些简单的方法可以减小页面大小并修复诊断问题。 在这篇文章中,你将学习如何通过实施四种高级策略来避免巨大的网络负载。 除了帮助您修复“避免过大的网络负载”诊断问题并提高您在 Google PageSpeed Insights 中的得分之外,这些技巧还能帮助您创建加载速度更快的网站并改善网站的用户体验。 看看这个简单的解决方法,它可以避免你的网站承受巨大的网络负载 什么是巨型网络有效载荷? 网络有效载荷是指在您的网站服务器和客户端浏览器之间传输的数据大小。 它是你的网站每次有人访问页面时都必须加载的数字“货物”。这包括用于构建内容结构的 HTML 文件、用于设置样式的 CSS 文件、用于添加交互功能的 JavaScript 文件,以及用于增强用户体验的媒体文件,例如图像和视频。 换句话说,它是使你的页面对人

By Ne0inhk
openclaw配置教程(linux+局域网ollama)

openclaw配置教程(linux+局域网ollama)

openclaw配置教程(linux+局域网ollama) 一、相关网站 ollama: * https://github.com/ollama/ollama * https://ollama.com/ openclaw: * https://openclaw.ai/ https://docs.openclaw.ai/ 二、前置条件 在本地局域网内的机器上已经部署了ollama ollama模型已下载,服务已经开启,可以通过curl http://local.ollama.host:11434/v1/model 获取到模型信息就行了 三、 环境 openclaw部署在一台linux机器A上 ollama部署在另一台机器 B上 本文中机器A是centos8 yum源已换、已经安装node22+,npm换源淘宝源 四、部署 1、用npm安装,然后进入部署界面

By Ne0inhk