C++智能指针的知识!

news2025/1/23 6:09:55

个人主页:PingdiGuo_guo

收录专栏:C++干货专栏

大家好呀,我是PingdiGuo_guo,今天我们来学习一下智能指针。

文章目录

1.智能指针的概念

2.智能指针的思想

3.智能指针的作用

3.1 自动内存管理

 3.2 共享所有权

3.3 避免悬挂指针

4.智能指针的操作

         4.1 初始化

4.2 拷贝与移动

4.3 访问与解引用

5.智能指针的分类

5.1 unique_ptr(独占指针)

5.2 shared_ptr(共享指针)

5.3 weak_ptr(弱指针)

5.4 auto_ptr(自动指针)

6.智能指针的练习

6.1题目

6.2步骤

6.3完整代码

7.注意事项

8.智能指针和常规指针的对比

8.1功能和使用方式

8.2内存管理

8.3安全性

8.4总结

9.总结


1.智能指针的概念


智能指针是在C++中用于管理堆内存的对象,它是C++标准库提供的一个抽象层,用来封装原始的裸指针。智能指针本质上是一个类,它有一个指向动态分配对象的成员变量,并且该类重载了运算符和其他必要的方法,以便在适当的时间自动释放所指向的对象。这相当于把资源管理的责任从程序员转移到了智能指针对象上,遵循了RAII(Resource Acquisition Is Initialization)的原则。


2.智能指针的思想


智能指针的核心思想在于将内存资源的生命周期绑定到某个特定的对象(智能指针对象)上,而不是依赖于程序执行流程中的某些逻辑判断来决定何时释放内存。当智能指针对象不再需要(例如,出了作用域或被删除时),它会自动清理自己管理的内存,从而消除因忘记释放内存而导致的内存泄漏问题。


3.智能指针的作用


3.1 自动内存管理

智能指针能确保在其生命周期结束后自动释放其管理的堆内存资源。

 3.2 共享所有权

某些类型的智能指针(如std::shared_ptr)支持多个智能指针共享同一份动态分配的内存,只有当所有共享此内存的智能指针都被销毁时,内存才会真正释放。

3.3 避免悬挂指针

智能指针通过维护内部计数器或其他机制,可以防止在内存已经被释放后仍访问该内存的情况,从而避免悬挂指针错误。



4.智能指针的操作

4.1 初始化

创建智能指针时,可以通过传递给构造函数一个new表达式的结果来初始化智能指针,使其开始管理一块内存。
 

std::unique_ptr<int> uptr(new int(42));



4.2 拷贝与移动


- 普通拷贝:对于std::unique_ptr来说,拷贝构造函数和赋值运算符被禁用,因为唯一所有权不允许复制;而对于std::shared_ptr,拷贝会增加引用计数。
- 移动操作:智能指针通常支持移动构造函数和移动赋值运算符,它们会转移资源的所有权,使得原智能指针变为空指针,而目标智能指针接管资源管理。



4.3 访问与解引用

智能指针提供了类似于普通指针的操作,可以通过->运算符访问成员,或者通过解引用运算符*直接访问对象。
 

std::cout << *uptr; // 输出42
uptr->some_method(); // 调用智能指针所指对象的方法

5.智能指针的分类

智能指针通常分为以下几类:

5.1 unique_ptr(独占指针)

unique_ptr是C++11引入的一种独占式智能指针,它通过使用“独占权”来确保资源的单一所有者。这意味着在同一时间只有一个unique_ptr可以拥有一个资源,并且当unique_ptr销毁时,它所拥有的资源会被自动释放。

#include <memory>

int main() {
  std::unique_ptr<int> ptr(new int);
  *ptr = 10;
  // unique_ptr会在作用域结束时自动释放资源
  return 0;
}

5.2 shared_ptr(共享指针)

shared_ptr是一种通过“引用计数”来共享资源的智能指针。它可以有多个shared_ptr同时拥有同一个资源,每个shared_ptr都会维护一个计数器,用于追踪资源的引用数。只有当引用计数降为0时,资源才会被释放。


 

#include <memory>

int main() {
  std::shared_ptr<int> ptr1(new int);
  std::shared_ptr<int> ptr2 = ptr1;
  *ptr1 = 10;
  *ptr2 = 20;
  // 此时引用计数为2
  return 0;
}

5.3 weak_ptr(弱指针)

weak_ptr是一种特殊的共享指针,它可以“观测”shared_ptr的资源,而不拥有该资源。weak_ptr并不会影响资源的生命周期,当其所观测的资源被释放后,weak_ptr会自动变为空指针。


 

#include <memory>

int main() {
  std::shared_ptr<int> ptr(new int);
  std::weak_ptr<int> weakPtr = ptr;
  // 此时weakPtr观测ptr所指向的资源
  ptr.reset();
  // 此时资源已被释放,weakPtr变为空指针
  return 0;
}

5.4 auto_ptr(自动指针)

auto_ptr是C++98中提供的一种智能指针,用于进行简单的资源管理。与unique_ptr类似,auto_ptr也是一种独占式的智能指针,但它的语义和行为比较特殊,容易引发一些不可预料的问题。由于其语义问题,auto_ptr在C++11中已被弃用。

#include <memory>

int main() {
  std::auto_ptr<int> ptr(new int);
  *ptr = 10;
  // auto_ptr会在作用域结束时自动释放资源
  return 0;
}


 

总结起来,智能指针是C++中一种方便且安全的资源管理工具。通过使用智能指针,我们能够避免手动管理内存资源的复杂性和潜在错误,提高代码的可靠性和可维护性。

6.智能指针的练习

6.1题目

实现一个具有引用计数功能的智能指针类,用于管理动态分配的整型数据。

6.2步骤

1. 创建一个智能指针类SmartPointer,其中包含一个指向整型数据的指针和一个整型的引用计数变量。在构造函数中,为指针分配内存并将引用计数初始化为1。

#include <iostream>

class SmartPointer {
public:
    SmartPointer(int* pData) : m_pData(pData), m_pCount(new int(1)) {
        std::cout << "Create SmartPointer with data " << *m_pData << std::endl;
    }
    // 省略其他成员函数
private:
    int* m_pData;
    int* m_pCount;
};

2. 实现拷贝构造函数,它将另一个SmartPointer对象作为参数。在拷贝构造函数中,将指针和引用计数变量分别指向原始对象的成员,并将引用计数递增。

SmartPointer(const SmartPointer& other) : m_pData(other.m_pData), m_pCount(other.m_pCount) {
    ++(*m_pCount);
    std::cout << "Copy SmartPointer with data " << *m_pData << std::endl;
}


3. 实现析构函数,用于在引用计数变为0时释放内存。在析构函数中,将引用计数递减,若引用计数为0,则释放指针指向的内存。

~SmartPointer() {
    --(*m_pCount);
    if (*m_pCount == 0) {
        delete m_pData;
        delete m_pCount;
        std::cout << "Delete SmartPointer with data " << *m_pData << std::endl;
    }
}


 

4. 重载赋值运算符,用于实现对象的赋值操作。在赋值运算符中,需要先递减原对象的引用计数,然后再递增赋值对象的引用计数。



 

SmartPointer& operator=(const SmartPointer& other) {
    if (this != &other) {
        --(*m_pCount);
        if (*m_pCount == 0) {
            delete m_pData;
            delete m_pCount;
            std::cout << "Delete SmartPointer with data " << *m_pData << std::endl;
        }
        m_pData = other.m_pData;
        m_pCount = other.m_pCount;
        ++(*m_pCount);
        std::cout << "Assign SmartPointer with data " << *m_pData << std::endl;
    }
    return *this;
}

5. 创建一个测试函数,在函数中创建多个SmartPointer对象,并进行赋值操作。

void test() {
    SmartPointer sp1(new int(5));
    SmartPointer sp2(sp1);
    SmartPointer sp3(new int(10));
    sp3 = sp2;
}


 

6. 在主函数中调用测试函数并观察输出结果。


 

int main() {
    test();
    return 0;
}

6.3完整代码


 

#include <iostream>

class SmartPointer {
public:
    SmartPointer(int* pData) : m_pData(pData), m_pCount(new int(1)) {
        std::cout << "Create SmartPointer with data " << *m_pData << std::endl;
    }
    
    SmartPointer(const SmartPointer& other) : m_pData(other.m_pData), m_pCount(other.m_pCount) {
        ++(*m_pCount);
        std::cout << "Copy SmartPointer with data " << *m_pData << std::endl;
    }
    
    ~SmartPointer() {
        --(*m_pCount);
        if (*m_pCount == 0) {
            delete m_pData;
            delete m_pCount;
            std::cout << "Delete SmartPointer with data " << *m_pData << std::endl;
        }
    }
    
    SmartPointer& operator=(const SmartPointer& other) {
        if (this != &other) {
            --(*m_pCount);
            if (*m_pCount == 0) {
                delete m_pData;
                delete m_pCount;
                std::cout << "Delete SmartPointer with data " << *m_pData << std::endl;
            }
            m_pData = other.m_pData;
            m_pCount = other.m_pCount;
            ++(*m_pCount);
            std::cout << "Assign SmartPointer with data " << *m_pData << std::endl;
        }
        return *this;
    }
    
private:
    int* m_pData;
    int* m_pCount;
};

void test() {
    SmartPointer sp1(new int(5));
    SmartPointer sp2(sp1);
    SmartPointer sp3(new int(10));
    sp3 = sp2;
}

int main() {
    test();
    return 0;
}

7.注意事项

使用智能指针时,有一些注意事项需要注意:

1.避免循环引用:循环引用指的是两个或多个对象相互持有对方的智能指针,导致引用计数始终不为0,从而造成内存泄漏。为了避免循环引用,应该尽量使用弱引用(std::weak_ptr)或者使用裸指针来解除循环引用。

2.避免使用裸指针:为了避免手动管理内存和潜在的内存泄漏,应该尽量使用智能指针来管理动态分配的内存。避免将裸指针传递给智能指针,以防止多个智能指针管理同一个内存块。

3.不要将裸指针和智能指针混合使用:在代码中,应该始终使用智能指针来管理动态分配的内存。避免将裸指针和智能指针混合使用,否则可能导致重复释放内存或内存泄漏。

4.使用std::make_sharedstd::make_unique来创建智能指针:std::make_sharedstd::make_unique是用来创建智能指针的函数模板,它们可以保证在创建智能指针时同时分配内存,避免了显式地使用new操作符。

5.当需要使用原始指针时,使用get获取原始指针:在某些情况下,可能需要将智能指针转换为原始指针。可以使用get成员函数来获取智能指针内部的原始指针。

6.避免在多线程中共享智能指针:智能指针的引用计数机制在多线程中可能引发竞争条件。如果需要在多线程中共享智能指针,应该采取适当的同步措施来确保线程安全。

总之,使用智能指针可以减少内存泄漏的风险,简化内存管理,但也需要注意上述的注意事项来避免潜在的问题。

8.智能指针和常规指针的对比

8.1功能和使用方式

智能指针:智能指针是C++标准库提供的一种数据类型,用于管理动态分配的内存资源。

智能指针可以自动释放内存资源,避免内存泄漏的问题。

智能指针一般采用引用计数的方式,当没有引用指向对象时,会自动释放内存。

智能指针可以像常规指针一样操作对象,可以通过操作符重载来访问对象的成员。

常规指针:常规指针是C/C++语言中的基本概念,用于存储对象的内存地址。

常规指针需要手动管理内存的申请和释放,容易出现内存泄漏和悬空指针的问题。

常规指针可以进行指针运算,如加减操作符来移动指针的位置。

8.2内存管理

智能指针:智能指针通过引用计数或其他方式来自动管理内存,在对象不再被引用时自动释放内存,避免了内存泄漏和悬空指针的问题。

常规指针:常规指针需要手动申请和释放内存,容易出现内存泄漏和悬空指针的问题。

8.3安全性

智能指针:智能指针具有类型安全性,可以在编译时检查类型是否匹配,避免了类型错误的问题。

常规指针:常规指针没有类型安全性,可以进行任意类型的转换,容易出现类型错误。

8.4总结

智能指针相比于常规指针具有自动内存管理、类型安全等优点,能够提高程序的可靠性和安全性。然而,智能指针也会带来一些额外的开销和循环引用等问题。常规指针更为灵活,但需要手动管理内存,容易出现内存泄漏和悬空指针等问题。选择使用智能指针还是常规指针要根据具体的需求和场景来决定。

9.总结

本篇博客到这里就结束了,感谢大家的支持与观看,如果有好的建议欢迎留言,谢谢大家啦!

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

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

相关文章

Linux第58步_备份busybox生成rootfs根文件系统

备份busybox生成rootfs根文件系统 打开终端 输入“ls回车” 输入“cd linux/回车” 输入“ls回车”&#xff0c;产看“linux”目录下的文件和文件夹 输入“cd nfs/回车”&#xff0c;切换到“nfs”目录 输入“ls回车”&#xff0c;产看“nfs”目录下的文件和文件夹 输入…

软件实例分享,台球厅收费系统电脑桌球店计时软件及灯控线路图教程

软件实例分享&#xff0c;台球厅收费系统电脑桌球店计时软件及灯控线路图教程 一、前言 以下软件程序教程以 佳易王桌球室计时计费软件V17.0为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 1、每个桌子对应的有 开台时间&#xff0c;所用的时…

为什么将二维码分解成文字? 二维码在线转文字的方法

将二维码分解成文字的主要目的是为了方便人们获取二维码中的信息便于使用。二维码是一种由黑白方块组成的图案&#xff0c;可以存储大量的数据&#xff0c;如网址、联系方式、产品信息等。然而&#xff0c;对于一些特定的场景或个人需求&#xff0c;无法直接扫描二维码。因此&a…

Java数字孪生智慧工地数据大屏APP项目源码

目录 智慧工地云平台核心功能 1.劳务管理 2.视频监控 3.安全教育 4.进度管理 5.环境监测 6.塔吊监控 7.升降机监控 8.工地广播 9.深基坑高支模 10.AI识别 11.安全质量 智慧工地建设的价值和意义 危大工程管理 智慧工地聚焦施工现场一线生产活动&#xff0c;利用物…

线性回归-使用ClickHouse机器学习函数

本文字数&#xff1a;5923&#xff1b;估计阅读时间&#xff1a;15 分钟 作者&#xff1a;Ensemble 审校&#xff1a;庄晓东&#xff08;魏庄&#xff09; 本文在公众号【ClickHouseInc】首发 这原本是转发的ensemble analytics的文章。 【https://ensembleanalytics.io/blog/l…

全网Bento和3D?点评2024年UX/UI设计趋势

2024年已经到来&#xff0c;对于UX/UI设计领域来说&#xff0c;这可能是过去若干年来UI / UX趋势最统一、最确定的一年。在接下来的文章中&#xff0c;笔者将在点评各个设计趋势的同时&#xff0c;分析现象背后的原因&#xff0c;并给新入行的设计师一些成长的建议。 什么是UI和…

CVE-2022-24652 漏洞复现

CVE-2022-24652 开题 后台管理是thinkphp的&#xff0c;但是工具没检测出漏洞。 登陆后界面如下&#xff0c;上传头像功能值得引起注意 这其实就是CVE-2022-24652&#xff0c;危险类型文件的不加限制上传&#xff0c;是文件上传漏洞。漏洞路由/user/upload/upload 参考文章&a…

《Java 简易速速上手小册》第1章:Java 编程基础(2024 最新版)

文章目录 1.1 Java 概述 - 不只是咖啡1.1.1 基础知识1.1.2 重点案例&#xff1a;网上银行应用1.1.3 拓展案例 1&#xff1a;电子商务平台1.1.4 拓展案例 2&#xff1a;物联网&#xff08;IoT&#xff09;系统 1.2 Java 开发环境设置 - 魔法开始的地方1.2.1 基础知识1.2.2 重点案…

【JavaEE】IP协议

作者主页&#xff1a;paper jie_博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文于《JavaEE》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和精力)打造&…

腾讯云OSS文件上传功能

腾讯云COS介绍 腾讯云COS&#xff08;Cloud Object Storage&#xff09;是一种基于对象的存储服务&#xff0c;用于存储和管理海量的非结构化数据&#xff0c;如图片、音视频文件、备份数据等。它具有以下特点和优势&#xff1a; 高可靠性&#xff1a;采用分布式存储架构&…

MCU看门狗

目录 一、独立看门狗(IWDG) 1、IWDG 主要作用 2、IWDG 主要特性 3、编程控制 4、注意地方 二、窗口看门狗(WWDG) 1、窗口看门狗作用&#xff1a; 2、窗口看门狗产生复位信号有两个条件&#xff1a; 3、WWDG 框图 4、WWDG 将要复位的时间 5、编程控制 一、独立看门…

揭秘京东商品背后的秘密:一键获取详细数据,打造全新购物体验

京东商品详情原数据API接口技术详解 一、概述 京东商品详情原数据API接口是京东开放平台提供的一套用于获取商品详细信息的接口。通过调用该接口&#xff0c;第三方开发者可以获取包括商品描述、价格、图片、评价等详细信息&#xff0c;进而在自己的应用或网站中展示给用户&a…

【STM32 物联网】AT指令与TCP,发送与接收数据

文章目录 前言一、连接TCP服务器1.1 配置Wifi模式1.2 连接路由器1.3 查询ESP8266设备IP地址1.4 连接TCP服务器 二、向服务器接收数据和发送数据2.1 发送数据2.2 接收数据 总结 前言 随着物联网&#xff08;IoT&#xff09;技术的迅速发展&#xff0c;越来越多的设备和系统开始…

Python:异常处理

异常处理已经成为判断一门编程语言是否成熟的标准&#xff0c;除传统的像C语言没有提供异常机制之外&#xff0c;目前主流的编程语言如Python、Java、Kotlin等都提供了成熟的异常机制。异常机制可以使程序中的异常处理代码和正常业务代码分离&#xff0c;保证代码更加优雅&…

机器学习第二十八周周报 PINNs2

文章目录 week28 PINNs2摘要Abstract一、Lipschitz条件二、文献阅读1. 题目数据驱动的偏微分方程2. 连续时间模型3. 离散时间模型4.结论 三、CLSTM1. 任务要求2. 实验结果3. 实验代码3.1模型构建3.2训练过程代码 小结参考文献 week28 PINNs2 摘要 本文主要讨论PINN。本文简要…

vue-路由(六)

阅读文章你可以收获什么&#xff1f; 1 明白什么是单页应用 2 知道vue中的路由是什么 3 知道如何使用vueRouter这个路由插件 4 知道如何如何封装路由组件 5 知道vue中的声明式导航router-link的用法 6 知道vue中的编程式导航的使用 7 知道声明式导航和编程式导航式如何传…

代码随想录算法训练营DAY19 | 二叉树 (6)

一、LeetCode 654 最大二叉树 题目链接&#xff1a;654.最大二叉树https://leetcode.cn/problems/maximum-binary-tree/ 思路&#xff1a;坚持左开右闭原则&#xff0c;递归划分数组元素生成左右子树。 class Solution {public TreeNode constructMaximumBinaryTree(int[] num…

数字人如何激发广电内容创新可能性?

在数字化传播时代&#xff0c;数字人的兴起对激发传统文化的内在活力具有重要意义。如河南广电以《唐宫夜宴》中唐代仕女为原型设计&#xff0c;推出身穿三彩襦裙的数字人“唐小妹”&#xff0c;目前数字人“唐小妹”已在河南卫视中国节日系列的多个节目中出现&#xff0c;带给…

【算法】C语言使用qsort对字符串字符进行排序(解决字符串长度不统一的问题)

核心思想是 当a的长度>b时&#xff0c;a一定比b大当a,b长度相等时&#xff0c;通过strcmp比较哪个字典序大&#xff0c;也就是实际的数哪个大当a的长度&#xff1c;b是&#xff0c;b一定比a大 这时候就不得不感慨C的string是多么好用的&#xff0c;哎 #include<stdio.h…

由于找不到MSVCP140.dll无法运行软件游戏,多种解决方法分享

电脑系统在运行过程中&#xff0c;当出现“由于找不到MSVCP140.dll”这一提示时&#xff0c;可能会引发一系列潜在的问题与影响。当电脑无法找到这个特定的dll文件时&#xff0c;意味着相关应用可能无法顺利加载并执行必要的组件&#xff0c;进而导致程序无法启动或运行过程中频…