【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

Java 大视界 -- Java 大数据在智能家居环境监测与智能调节中的应用拓展(423)

Java 大视界 -- Java 大数据在智能家居环境监测与智能调节中的应用拓展(423)

Java 大视界 -- Java 大数据在智能家居环境监测与智能调节中的应用拓展(423) * 引言: * 快速上手指南:3 步跑通智能家居 Demo(新手友好) * Step 1:环境准备(必装软件清单) * Step 2:代码运行(按顺序执行) * Step 3:效果验证(用 Postman 模拟数据) * 正文: * 一、智能家居环境监测与调节的核心痛点 * 1.1 设备数据的 “异构化” 困境 * 1.1.1 多源数据的 “协议壁垒” * 1.1.2 数据规模的 “爆发式增长” * 1.2 实时调节的 “滞后性” 痛点 * 1.

By Ne0inhk
计算机毕业设计springboot中小型制造型企业erp管理系统 基于Spring Boot的中小型制造企业资源计划系统设计与实现 基于Java的中小型生产企业运营管控平台开发

计算机毕业设计springboot中小型制造型企业erp管理系统 基于Spring Boot的中小型制造企业资源计划系统设计与实现 基于Java的中小型生产企业运营管控平台开发

计算机毕业设计springboot中小型制造型企业erp管理系统0t90v9 (配套有源码 程序 mysql数据库 论文) 本套源码可以在文本联xi,先看具体系统功能演示视频领取,可分享源码参考。 本系统采用Spring Boot作为核心开发框架,结合MySQL数据库与Tomcat服务器,构建B/S架构的Web应用平台。系统严格遵循MVC设计模式,将业务逻辑、数据访问与视图展示层解耦,确保代码的高内聚低耦合与良好的可扩展性。前端界面设计注重用户体验,采用简洁直观的交互风格,降低员工学习成本。系统支持Windows操作系统下的稳定运行,兼容主流浏览器访问,满足企业多场景、跨终端的办公需求。 系统核心功能涵盖以下模块: 员工管理模块:实现员工基础信息的录入、查询、修改与删除,包含员工工号、姓名、性别、年龄、联系方式、所属部门、职务等核心字段,支持员工账号的注册与登录验证。 人事档案模块:记录员工详细人事信息,包括入职日期、个人照片、职务履历、个人档案材料等,支持档案的电子化管理与快速检索。 部门信息管理模块:维护企业组织架构,支持部门名称的增删改查,构建清晰的部门层级关系。 员

By Ne0inhk
飞算JavaAI全链路实战:智能构建高可用电商系统核心架构

飞算JavaAI全链路实战:智能构建高可用电商系统核心架构

飞算JavaAI全链路实战:智能构建高可用电商系统核心架构 前言:AI编程新时代的电商系统开发范式变革 最近学习人工智能时遇到一个好用的网站给大家分享一下 人工智能学习 在当今数字经济时代,电商系统作为企业数字化转型的核心载体,其复杂度和技术要求与日俱增。一个完整的电商系统不仅需要处理商品、订单、用户等基础业务,还要应对高并发、分布式事务、数据一致性等复杂技术挑战。传统开发模式下,从需求分析到系统上线往往需要耗费大量人力和时间成本。 本次我通过飞算JavaAI平台,深入探索"电商系统核心功能模块"这一实战赛道,全面体验了从需求分析到代码生成的全链路开发过程。本文将完整呈现如何借助AI辅助开发工具,高效构建一个包含用户管理、商品系统、订单流程、支付集成等核心模块的电商平台,严格遵循"需求分析-开发实录-优化调试-成果总结"的四大核心框架,为开发者提供一份AI辅助全栈开发的完整实践指南。 一、需求分析与规划:构建电商系统的业务架构蓝图 在启动飞算JavaAI之前,需要进行全面的业务需求梳理和系统架构设计,这是确保AI生成代码符合预期的基础。 1.(理解需求)系统核心模块与

By Ne0inhk
【2025年最新版】Java JDK安装、环境配置教程 (图文非常详细)

【2025年最新版】Java JDK安装、环境配置教程 (图文非常详细)

文章目录 * 【2025年最新版】Java JDK安装、环境配置教程 (图文非常详细) * 1. JDK介绍 * 2. 下载 JDK * 3. 安装 JDK * 4. 配置环境变量 * 5. 验证安装 * 6. 创建并测试简单的 Java 程序 * 6.1 创建 Java 程序: * 6.2 编译和运行程序: * 6.3 在显示或更改文件的扩展名(文件后缀) 【2025年最新版】Java JDK安装、环境配置教程 (图文非常详细) 1. JDK介绍 JDK(Java Development Kit) 是 Java 程序开发的核心工具包,包含了开发 Java

By Ne0inhk