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.mzph.cn/news/688114.shtml

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

相关文章

【多线程】线程的概念与创建

多线程 1. 认识线程&#xff08;Thread&#xff09;线程是什么为啥要有线程进程和线程的区别Java 的线程 和 操作系统线程 的关系 2.第⼀个多线程程序3.创建线程⽅法1 继承 Thread 类⽅法2 实现 Runnable 接⼝方法3 匿名内部类创建 Thread ⼦类对象方法4 匿名内部类创建 Runnab…

python-自动化篇-办公-批量新建文件夹并保存日志信息

文章目录 说明代码效果 说明 因为业务需要&#xff0c;每天都需要按当天的日期创建很多新文件夹。把这种重复又繁重的操作交给Python来做&#xff0c;一直是我的目标。先说下要求&#xff1a; 默认在桌面新建文件夹。文件夹命名方式&#xff0c;“月.日-1”&#xff0c;比如7…

transformer 最简单学习1 输入层embeddings layer

词向量的生成可以通过嵌入层&#xff08;Embedding Layer&#xff09;来完成。嵌入层是神经网络中的一种常用层&#xff0c;用于将离散的词索引转换为密集的词向量。以下是一个典型的步骤&#xff1a; 建立词表&#xff1a;首先&#xff0c;需要从训练数据中收集所有的词汇&…

Instagram 账号被封如何申诉?ins账号解封经验分享

不知道各位在玩转海外社媒平台时有没有遇到过Instagram账号异常的情况&#xff0c;比如会出现账号受限、帖子发不出去、账号被封号等情况?Instagram账号如果被封不用马上弃用&#xff0c;我们可以先尝试一下申诉&#xff0c;看看能不能把账号解封。所以今天将会出一篇Instagra…

开工大吉!秀一下我们假期の战绩

开工大吉&#xff0c;新年新气象 首先祝大家开工大吉&#xff0c;新年新气象。 祝我的粉丝股东们都能&#xff1a;顺利上岸&#xff0c;升职加薪&#xff0c;日进斗金&#xff01; 开工就要冲冲冲&#xff01; 春节假期我是好好放松了&#xff0c;在努力克制自己不要像之前…

VSCODE上使用python_Django

接上篇 https://blog.csdn.net/weixin_44741835/article/details/136135996?csdn_share_tail%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22136135996%22%2C%22source%22%3A%22weixin_44741835%22%7D VSCODE官网&#xff1a; Editing Python …

My desktop didn‘t come with the Bluetooth.

You didnt turn on the Bluetooth on PC and phone.Turn on it to control your phone. My desktop didnt come with the Bluetooth. 电脑控制手机的时候&#xff0c;电脑蓝牙没打开 电脑蓝牙打开步骤 电脑蓝牙的小图标打开了 手机上可以看到计算机了【Thinkpad-T440p-zwf】 无…

Java面向对象案例之设计用户去ATM机存款取款(三)

需求及思路分析 业务代码需求&#xff1a; 某公司要开发“银行管理系统”&#xff0c;请使用面向对象的思想&#xff0c;设计银行的储户信息&#xff0c;描述存款、取款业务。 储户类的思路分析&#xff1a; 属性&#xff1a;用户姓名、密码、身份证号、账号、帐户余额 方法&a…

【自然语言处理】实验3,文本情感分析

清华大学驭风计划课程链接 学堂在线 - 精品在线课程学习平台 (xuetangx.com) 代码和报告均为本人自己实现&#xff08;实验满分&#xff09;&#xff0c;只展示主要任务实验结果&#xff0c;如果需要详细的实验报告或者代码可以私聊博主 有任何疑问或者问题&#xff0c;也欢…

洛谷P5719 分类平均 题解

#题外话&#xff08;第32篇题解&#xff09; #先看题目 题目链接https://www.luogu.com.cn/problem/P5719 #思路&#xff08;注意一下分类计数、i<n这些问题就行&#xff09; #代码 #include <bits/stdc.h> using namespace std; int main() {int n,k,cnt0;double…

第13章 网络 Page741~744 asio核心类 ip::tcp::socket

1. ip::tcp::socket liburl库使用"curl*" 代表socket 句柄 asio库使用ip::tcp::socket类代表TCP协议下的socket对象。 将“句柄”换成“对象”,因为asio库是不打折扣的C库 ip::tcp::socket提供一下常用异步操作都以async开头 表13-3 tcp::socket提供的异步操作 …

[嵌入式系统-28]:开源的虚拟机监视器和仿真器:QEMU(Quick EMUlator)与VirtualBox、VMware Workstation的比较

目录 一、QEMU概述 1.1 QEMU架构 1.2 QEMU概述 1.3 什么时候需要QEMU 1.4 QEMU两种操作模式 1.5 QEMU模拟多种CPU架构 二、QEMU与其他虚拟机的比较 2.1 常见的虚拟化技术 2.1 Linux KVM 2.2 Windows VirtualBox 2.3 Windows VMware workstation 三、VirtualBox、VM…

Vue22 Vue监测数据改变的原理_数组

实例 <!DOCTYPE html> <html><head><meta charset"UTF-8" /><title>Vue监测数据改变的原理_数组</title><!-- 引入Vue --><script type"text/javascript" src"../js/vue.js"></script>&…

fish终端下conda activate失败

【问题】fish终端下激活conda环境报错&#xff1a; >> conda activate base CondaError: Run conda init before conda activate ## 然而运行 conda init fish 仍旧无法解决【解决】 参考&#xff1a;https://github.com/conda/conda/issues/11079 方法一&#xf…

2024.02.18作业

1. 使用fgets统计给定文件的行数 #include <stdio.h> #include <stdlib.h> #include <string.h>int main(int argc, char const *argv[]) {if (argc ! 2){puts("input file error");puts("usage:./a.out filename");return -1;}FILE* f…

CSP-201812-1-小明上学

CSP-201812-1-小明上学 解题思路 #include <iostream> using namespace std; int main() {int red, yellow, green, n, timeSum 0;cin >> red >> yellow >> green;cin >> n;for (int i 0; i < n; i){int flag, time;cin >> flag &g…

分享视频二维码如何生成?怎么让其他人扫码查看自己的视频?

现在很多展示性的视频都会通过生成二维码的方式来传播分享&#xff0c;选择二维码的方式来分享能够更加快捷的获取视频内容&#xff0c;让更多人可以同时查看&#xff0c;有效提高视频传播的速度和效率。那么怎么制作视频二维码呢&#xff1f;其实方法很简单&#xff0c;只需要…

lvs DR模式+基于五台服务器部署keepalived + lvs DR模式架构(前端带路由)负载均衡的高可用集群

lvs DR模式基于五台服务器部署keepalived lvs DR模式架构(前端带路由)负载均衡的高可用集群 DR模式一&#xff1a; 客户端&#xff1a;172.20.26.167 LVS服务器&#xff1a;172.20.26.198 后端服务器&#xff1a;172.20.26.218 后端服务器&#xff1a;172.20.26.210 两台…

三勾点餐系统源码,java后台+微信小程序 实现完整的餐厅点餐

三勾点餐系统基于javaspringbootelement-plusuniapp打造的面向开发的小程序商城&#xff0c;方便二次开发或直接使用&#xff0c;可发布到多端&#xff0c;包括微信小程序、微信公众号、QQ小程序、支付宝小程序、字节跳动小程序、百度小程序、android端、ios端。 功能介绍 1.…

【牛客面试必刷TOP101】Day23.BM27 按之字形顺序打印二叉树和BM30 二叉搜索树与双向链表

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;牛客面试必刷TOP101 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01;&…