C++智能指针的知识!

个人主页: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.总结

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

春节如此“热辣滚烫”,可别把赚钱的机会都让给别人!2024如果创业适合干什么,2024创业新风口

春节期间&#xff0c;在国外抨击我们的旅客不出去旅行&#xff0c;造成了1300亿损失的时候&#xff0c;国内的消费可谓是“热辣又滚烫”&#xff0c;一片勃勃生机的景象。 各个地方纷纷的“亮底牌、放大招”&#xff0c;举办各式各样丰富多彩的文化活动&#xff0c;还进行“农…

自动驾驶中的 DCU、MCU、MPU、SOC 和汽车电子架构

自动驾驶中的 DCU、MCU、MPU 1. 分布式电子电气架构2. 域集中电子电气架构架构2.1 通用硬件定义 3. 车辆集中电子电气架构4. ADAS/AD系统方案演变进程梳理4.1 L0-L2级别的ADAS方案4.2 L2以上级别的ADAS方案 5. MCU和MPU区别5.1 MCU和MPU的区别5.2 CPU与SoC的区别5.3 举个例子 R…

力扣题目训练(15)

2024年2月8日力扣题目训练 2024年2月8日力扣题目训练507. 完美数520. 检测大写字母521. 最长特殊序列 Ⅰ221. 最大正方形237. 删除链表中的节点115. 不同的子序列 2024年2月8日力扣题目训练 2024年2月8日第十五天编程训练&#xff0c;今天主要是进行一些题训练&#xff0c;包括…

基于Robei EDA--实现串口数据包接收

一、定义串口传输协议帧 控制字的数据大小为一字节&#xff0c;定义帧头为&#xff08;0xFE 0xDF&#xff09;帧尾为&#xff08;0xEF&#xff09; 模块框图 内模块&#xff1a;串口接收&#xff0c;output&#xff1a;8位data 串口命令&#xff1a;对单字节数据接收进行缓存…

去掉图片水印但是不伤原图?看完这些方法就知道了

小伙伴们&#xff0c;你们是不是经常在网上找一些好看的图片作为壁纸呢&#xff1f;有时候会遇到一些带着平台水印的图片&#xff0c;是不是觉得不太美观呢&#xff1f;别着急&#xff0c;其实我们可以使用一些去水印软件来将这些水印去除掉&#xff0c;让图片更加美观。那么&a…

贝塞尔曲线

一条 n n n 次贝塞尔曲线可以表示为 C ( u ) ∑ i 0 n B i , n ( u ) P i , 0 ≤ u ≤ 1 (1) \pmb C(u)\sum_{i0}^nB_{i,n}(u)\pmb{P_i},\quad 0\leq u\leq1\tag{1} C(u)i0∑n​Bi,n​(u)Pi​,0≤u≤1(1) 其中&#xff0c;基函数&#xff08;也称为混合函数&#xff09; {…

电商行业的机遇在哪?致淘宝平台API数据接口

在电商行业蓬勃发展的今天&#xff0c;我们不得不提及淘宝这个伟大的平台。它不仅为亿万用户提供了便捷的购物体验&#xff0c;更为无数的商家创造了一个财富的聚集地。而如今&#xff0c;随着技术的不断进步&#xff0c;淘宝开放了其强大的API接口&#xff0c;为广大开发者带来…

【开工大吉】推荐4款开源美观的WPF UI组件库

前言 经常有小伙伴在技术群里提问&#xff1a;WPF有什么好用的UI组件库&#xff1f;,今天大姚给大家推荐4款开源、美观的WPF UI组件库。 WPF介绍 WPF 是一个强大的桌面应用程序框架&#xff0c;用于构建具有丰富用户界面的 Windows 应用。它提供了灵活的布局、数据绑定、样式…

git分布式版本控制工具基本操作

Windows操作 1.1 git基本操作 1.设置用户签名 git config user.name xx git config user.email xxb163.com2.初始化本地库 git init3.查看本地库状态 git status4.添加暂存区 git add 文件名称 git add *5.提交本地库 git commit -m "描述信息" 文件6.查看版本…

基于SFLA算法的神经网络优化matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 SFLA的基本原理 4.2 神经网络优化 5.完整程序 1.程序功能描述 基于SFLA算法的神经网络优化。通过混合蛙跳算法&#xff0c;对神经网络的训练进行优化&#xff0c;优化目标位神经网络的…

springboot200个人博客系统的设计与实现

基于spring boot的个人博客系统的设计与实现 摘 要 随着国内市场经济这几十年来的蓬勃发展&#xff0c;突然遇到了从国外传入国内的互联网技术&#xff0c;互联网产业从开始的群众不信任&#xff0c;到现在的离不开&#xff0c;中间经历了很多挫折。本次开发的个人博客系统&a…

Day21--learning English

一、积累 1.stipulate 2.brutal 3.tatter 4.thermal 5.turnstile 6.tickle 7.patsy 8.extinguisher 9.leave well enough alone 10.tangle 11.twirl 12.despise 13.Hang out 14.crisp 15.dye 16.cement 17.stationery 18.sophisticated 19.gradient 20.funeral 二、练习 1.牛…

面试经典150题——螺旋矩阵

"The harder the conflict, the more glorious the triumph." - Thomas Paine 1. 题目描述 2. 题目分析与解析 2.1 思路一 看到题目&#xff0c;先仔细观察矩阵&#xff0c;题目要求我们给出顺时针遍历的结果即可&#xff0c;我们根据矩阵可以看出&#xff0c;首…

开源软件:推动软件行业繁荣的力量

文章目录 &#x1f4d1;引言开源软件的优势分析开放性与透明度低成本与灵活性创新与协作 开源软件对软件行业的影响推动技术创新和进步促进软件行业的合作与交流培养人才和提高技能促进软件行业的可持续发展 结语 &#x1f4d1;引言 随着信息技术的飞速发展&#xff0c;软件已经…

Java Remote Debug(远程调试)

【华邦云使用过】&#xff1a; -agentlib:jdwptransportdt_socket,address9090,servery,suspendn SpringBoot远程Debug步骤 配置Maven 首先在Maven的pom.xml中配置好如下信息&#xff1a; <project> ... <build> ... <plugins> ... <plugin> &…

机器学习 | 实现图像加密解密与数字水印处理

目录 实现窗口可视化 数字图像加密 窗口布局设置 基于混沌Logistic的图像加密 基于三重DES的图像加密 数字图像解密 窗口布局设置 基于混沌Logistic的图像解密 基于三重DES的图像解密 基于LSB的数字水印提取 窗口布局设置 水印的嵌入与提取 实现窗口可视化 这里…

【力扣 - 环形链表】

题目描述 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&a…

Chrome浏览器安装Axure-Chrome-Extension插件

Chrome浏览器打开Axure生成的HTML静态文件页面时&#xff0c;会显示如下图AXURE RP EXTENSION FOR CHROME&#xff0c;这是因为Chrome浏览器没有安装Axure插件Axure-Chrome-Extension导致的。 解决方法&#xff1a; 插件下载地址&#xff1a;https://download.csdn.net/downlo…

传日本软银CEO拟筹资成立AI芯片公司 | 百能云芯

据美国财经媒体报道&#xff0c;日本软体银行集团执行长孙正义计划筹资1,000亿美元&#xff0c;以成立一家人工智能&#xff08;AI&#xff09;芯片公司&#xff0c;这一举措旨在与英伟达&#xff08;Nvidia&#xff09;等现有市场领导者竞争。 计划的核心目标是通过新公司供应…

C语言系列-预定义符号#define定义宏#define定义宏

&#x1f308;个人主页: 会编辑的果子君 &#x1f4ab;个人格言:“成为自己未来的主人~” 目录 预定义符号 #define定义常量 #define定义宏 预定义符号 C语言设置了一些预定义符号&#xff0c;可以直接使用&#xff0c;预定义符号也是在预处理期间处理的。 __FILE__ /…