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;// 此时引用计数为2return 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.hqwc.cn/news/474973.html

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

相关文章

【开源】SpringBoot框架开发校园疫情防控管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 学生2.2 老师2.3 学校管理部门 三、系统展示四、核心代码4.1 新增健康情况上报4.2 查询健康咨询4.3 新增离返校申请4.4 查询防疫物资4.5 查询防控宣传数据 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBoot…

React 更改程序入口点(index.js文件位置变更)

食用前提示&#xff1a;本文基于已经快速配置好的React环境而作&#xff0c;配置React环境详见拙作&#xff1a;React环境配置-CSDN博客~ 一、了解默认入口点 使用create-react-app快速搭建react环境后&#xff0c;npm start启动程序的默认入口点为/src/index(即src目录下的ind…

ChatGPT的大致原理

国外有个博主写了一篇博文&#xff0c;名字叫TChatGPT: Explained to KidsQ」&#xff0c; 直译过来就是&#xff0c;给小孩子解释什么是ChatGPT。 因为现实是很多的小孩子已经可以用父母的手机版ChatGPT玩了 &#xff0c;ChatGPT几乎可以算得上无所不知&#xff0c;起码给小孩…

Linux——信号(1)

在我们使用Linux系统的时候我们经常会使用ctrl c的方式来终止进程&#xff0c;也 会使用kill命令来杀掉进程&#xff0c;评判进程退出的健康程度中也有信号的身影。那 么Linux中的信号到底是什么&#xff1f;今天就由我来介绍Linux中的信号。1. 信号的概念 要了解计算机中的信…

一.重新回炉spring框架: 理解Spring IoC

1. 写在前面的话 说实话&#xff0c;重试java开发工作时间也不短了&#xff0c;对于spring框架&#xff0c;也是天天用&#xff0c;这期间也碰到了很多问题&#xff0c;也解决了很多问题。可是&#xff0c;总感觉对spring还是一知半解&#xff0c;不能有个更加全面的理解。既然…

C++初阶:容器适配器介绍、stack和queue常用接口详解及模拟实现

介绍完了list类的相关内容后&#xff1a;C初阶&#xff1a;适合新手的手撕list&#xff08;模拟实现list&#xff09; 接下来进入新的篇章&#xff0c;stack和queue的介绍以及模拟&#xff1a; 文章目录 1.stack的初步介绍2.stack的使用3.queue的初步介绍4.queue的使用5.容器适…

SpringBoot实现OneDrive文件上传

SpringBoot实现OneDrive文件上传 源码 OneDriveUpload: SpringBoot实现OneDrive文件上传 获取accessToken步骤 参考文档&#xff1a;针对 OneDrive API 的 Microsoft 帐户授权 - OneDrive dev center | Microsoft Learn 1.访问Azure创建应用Microsoft Azure&#xff0c;使…

阿里云香港服务器cn2速度测试和租用价格表

阿里云香港服务器中国香港数据中心网络线路类型BGP多线精品&#xff0c;中国电信CN2高速网络高质量、大规格BGP带宽&#xff0c;运营商精品公网直连中国内地&#xff0c;时延更低&#xff0c;优化海外回中国内地流量的公网线路&#xff0c;可以提高国际业务访问质量。阿里云服务…

百度智能云分布式数据库 GaiaDB-X 与龙芯平台完成兼容认证

近日&#xff0c;百度智能云的分布式关系型数据库软件 V3.0 与龙芯中科技术股份有限公司的龙芯 3C5000L/3C5000 处理器平台完成兼容性测试&#xff0c;功能与稳定性良好&#xff0c;获得了龙架构兼容互认证证书。 龙芯系列处理器 通用 CPU 处理器是信息产业的基础部件&#xf…

黑马鸿蒙教程学习1:Helloworld

今年打算粗略学习下鸿蒙开发&#xff0c;当作兴趣爱好&#xff0c;通过下华为那个鸿蒙开发认证&#xff0c; 发现黑马的课程不错&#xff0c;有视频和完整的代码和课件下载&#xff0c;装个devstudio就行了&#xff0c;建议32G内存。 今年的确是鸿蒙大爆发的一年呀&#xff0c;…

从kafka如何保证数据一致性看通常数据一致性设计

一、前言 在数据库系统中有个概念叫事务&#xff0c;事务的作用是为了保证数据的一致性&#xff0c;意思是要么数据成功&#xff0c;要么数据失败&#xff0c;不存在数据操作了一半的情况&#xff0c;这就是数据的一致性。在很多系统或者组件中&#xff0c;很多场景都需要保证…

Docker的介绍、安装与常用命令

Docker的介绍、安装与常用命令 一、介绍1.相关资源2.安装环境3.基本组成 二、Docker安装1.检查系统环境2 安装gcc3 卸载旧版本docker4 安装软件包5 设置镜像仓库6 更新yum 索引7 安装&#xff08;ce版&#xff09;8 启动Docker9 阿里云镜像加速10 Docker卸载 三、 常用命令1 帮…