【C++】C++11——智能指针、内存泄漏、智能指针的使用和原理、RAII、auto_ptr、unique_ptr、shared_ptr、weak_ptr
文章目录
- C++11
- 7.智能指针
- 7.1内存泄漏
- 7.2智能指针的概念
- 7.3智能指针的使用
- 7.3.1 auto_ptr
- 7.3.2 unique_ptr
- 7.3.3 shared_ptr
- 7.3.4 weak_ptr
C++11
7.智能指针
7.1内存泄漏
什么是内存泄漏:
内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
内存泄漏通常由于程序在设计上的缺陷或错误,例如动态分配内存后,未在合适的时间或无法正确释放该段内存,而导致的。内存泄漏通常需要程序员通过分析程序源代码来识别和修复。
内存泄漏的危害:
在某些情况下,内存泄漏可能并不严重,或者能够被常规手段检测出来。然而,如果内存泄漏持续存在并导致大量可用内存被分配掉,可能会导致计算机性能显著下降,甚至导致全部或部分设备停止正常工作,或者应用程序崩溃。如果长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。
内存泄漏的示例:
void MemoryLeaks()
{// 1.内存申请了忘记释放int* p1 = (int*)malloc(sizeof(int));int* p2 = new int;// 2.异常安全问题int* p3 = new int[10];Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.delete[] p3;
}
如何避免内存泄漏:
(1)合理使用动态内存:
尽量避免使用动态内存分配,因为使用动态内存分配后需要手动释放内存,否则会造成内存泄漏。如果必须使用动态内存分配,应该确保在使用完毕后及时释放内存。
(2)及时释放内存:
在使用完动态分配的内存后,必须及时释放它们,以避免内存泄漏。在C++中,可以在析构函数中释放内存;在C中,可以使用free()函数来释放内存。
(3)使用智能指针:
智能指针是一种能够自动管理内存的机制,可以避免由于忘记释放内存而导致的内存泄漏。 C++11标准库提供了多种类型的智能指针,如unique_ptr、shared_ptr和weak_ptr,可以根据具体情况选择合适的智能指针类型来管理内存。
(4)尽量避免循环引用:
如果两个对象之间存在相互引用关系,并且没有任何其他对象或机制来打破循环引用,那么就会导致内存泄漏。因此,在设计程序时应该尽可能避免循环引用,或者使用其他机制来打破循环引用。
(5)使用垃圾回收机制:
一些编程语言提供了垃圾回收机制,可以自动检测并回收不再使用的内存。这些语言包括Java、Python和JavaScript等。如果使用这些语言,可以充分利用它们的垃圾回收机制来避免内存泄漏。
(6)定期检查程序:
定期检查程序是否存在内存泄漏是一种有效的手段。可以使用一些工具来检测程序中的内存泄漏, 例如Valgrind和AddressSanitizer等。这些工具可以帮助发现程序中的内存泄漏问题,并提供修复建议。
7.2智能指针的概念
智能指针是一种C++中的对象,它用于管理动态分配的内存,并自动释放内存以避免内存泄漏。智能指针的工作原理是,当智能指针超出其作用域或被销毁时,它会自动释放它所管理的内存。
智能指针的常见类型包括unique_ptr、shared_ptr和weak_ptr。 unique_ptr表示只有一个指针指向动态分配的内存,当unique_ptr被销毁时,它所管理的内存会被自动释放。shared_ptr表示多个指针可以指向同一块动态分配的内存,当最后一个指向该内存的shared_ptr被销毁时,它所管理的内存会被自动释放。weak_ptr是shared_ptr的一个别名,它不持有动态分配的内存的任何所有权,而是用于与其他智能指针一起使用,以避免循环引用。
下面是一个简单的unique_ptr使用的例子:
在这个例子中,我们首先定义了一个名为MyStruct的结构体,它有一个整数值。接下来,在main函数中,我们使用std::unique_ptr< MyStruct >类型定义了一个智能指针对象ptr,并使用new操作符分配了一个新的MyStruct对象,将它的指针赋给了ptr。我们可以通过ptr->value来访问MyStruct对象的值。最后,当main函数返回时,ptr将自动释放它所管理的内存,避免了内存泄漏。
#include <iostream>
#include <memory> struct MyStruct { int value; MyStruct(int v) : value(v) {}
}; int main() { std::unique_ptr<MyStruct> ptr(new MyStruct(10)); std::cout << "Value: " << ptr->value << std::endl; return 0;
}
7.3智能指针的使用
RAII:
RAII是资源获取就是初始化的简称,是C++语言的一种管理资源、避免泄漏的惯用法。
RAII的核心思想是,定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理。
这样可以保证资源的正确初始化和释放,防止内存泄漏。
利用C++构造的对象最终会被销毁的原则,当对象被销毁时,会自动调用其析构函数,释放对象生命期内控制的资源。
RAII有两个好处:
(1)不需要显式地释放资源;
(2)采用这种方式,对象所需的资源在其生命期内始终保持有效。
// 使用RAII思想设计的SmartPtr类
template<class T>
class SmartPtr
{
public:SmartPtr(T* ptr = nullptr): _ptr(ptr){}~SmartPtr(){if(_ptr)delete _ptr;}private:T* _ptr;
};int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}void Func()
{ShardPtr<int> sp1(new int);ShardPtr<int> sp2(new int);cout << div() << endl;
}int main()
{try {Func();}catch(const exception& e){cout<<e.what()<<endl;}return 0;
}
上述的SmartPtr还不能将其称为智能指针,因为它还不具有指针的行为。指针可以解引用,也可以通过->去访问所指空间中的内容,因此:AutoPtr模板类中还得需要将* 、->重载下,才可让其像指针一样去使用。
总结一下智能指针的原理:
(1)RAII特性
(2)重载operator*和opertaor->,具有像指针一样的行为
template<class T>
class SmartPtr
{
public:SmartPtr(T* ptr = nullptr): _ptr(ptr){}~SmartPtr(){if(_ptr)delete _ptr;}T& operator*() {return *_ptr;}T* operator->() {return _ptr;}private:T* _ptr;
};struct Date
{int _year;int _month;int _day;
};int main()
{SmartPtr<int> sp1(new int);*sp1 = 10cout<<*sp1<<endl;SmartPtr<int> sparray(new Date);// 需要注意的是这里应该是sparray.operator->()->_year = 2018;// 本来应该是sparray->->_year这里语法上为了可读性,省略了一个->sparray->_year = 2018;sparray->_month = 1;sparray->_day = 1;
}
7.3.1 auto_ptr
std::auto_ptr
C++98版本的库中就提供了auto_ptr的智能指针。
注意:auto_ptr被认为是一个失败的设计:
(1)所有权的转移不清晰:
auto_ptr在构造时获取对象的所有权,但可以通过赋值操作将所有权转移给另一个auto_ptr。这样会造成原来的auto_ptr指针悬空,程序直接崩溃。
(2)无法在STL容器中使用:
auto_ptr的拷贝构造函数会进行所有权的转移,这使得它难以被用在STL容器中。因为在容器中添加元素时,会涉及到拷贝操作,这可能会导致所有权的转移,从而使得容器中的元素所指向的对象被释放,引起程序错误。
(3)nullptr的问题:
auto_ptr无法安全地处理nullptr。如果你试图让一个auto_ptr指向一个nullptr,它会在析构时释放该指针,即使该指针从未被分配过内存。
(4)不支持自定义删除器:
auto_ptr只能使用默认的delete操作符来释放内存,无法使用自定义的删除器。这限制了auto_ptr的灵活性。基于以上原因,现代C++编程中更推荐使用unique_ptr、shared_ptr和weak_ptr来替代auto_ptr。
auto_ptr的实现原理:管理权转移的思想,下面简化模拟实现了一份bit::auto_ptr来了解它的原理:
template<class T>
class auto_ptr
{
public:auto_ptr(T* ptr):_ptr(ptr){}~auto_ptr(){cout << "delete:" << _ptr << endl;delete _ptr;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}//管理权转移auto_ptr(auto_ptr<T>& ap):_ptr(ap._ptr){ap._ptr = nullptr;}private:T* _ptr;
};
7.3.2 unique_ptr
std::unique_ptr
unique_ptr是C++11标准库中的一种智能指针,它持有对对象的独有权,即两个unique_ptr不能指向同一个对象,不能进行复制操作,只能进行移动操作。 当unique_ptr超出其作用域或被销毁时,它所管理的内存会被自动释放,有效避免了内存泄漏问题。unique_ptr是一种方便、安全、自动管理内存的机制,可以有效避免内存泄漏问题。
unique_ptr的实现原理:简单粗暴的防拷贝,下面简化模拟实现了一份unique_ptr来了解它的原理:
template<class T>
class unique_ptr
{
public:unique_ptr(T* ptr):_ptr(ptr){}~unique_ptr(){cout << "delete:" << _ptr << endl;delete _ptr;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}//禁止拷贝unique_ptr(unique_ptr<T>& ap) = delete;unique_ptr<T>& operator=(unique_ptr<T>& ap) = delete;private:T* _ptr;
};
7.3.3 shared_ptr
std::shared_ptr
shared_ptr是一种C++中的智能指针,它可以记录有多少个shared_ptrs指向一个对象。当最后一个这样的指针被销毁时,也就是最后一个指向对象的shared_ptr被销毁时,该对象会被自动删除,这在非环形数据结构中防止资源泄露很有帮助。
它与unique_ptr不同,shared_ptr允许多个指针指向同一个对象,并共同拥有该对象的所有权。
当最后一个shared_ptr被销毁时,它所指向的对象也会被自动删除。
shared_ptr的实现原理:
shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。
如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源; 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。
shared_ptr的实现原理:使用了引用计数,下面简化模拟实现了一份shared_ptr来了解它的原理:
template<class T>
class shared_ptr
{
public:// RAIIshared_ptr(T* ptr = nullptr):_ptr(ptr), _pcount(new int(1)){}~shared_ptr(){if (--(*_pcount) == 0){cout << "delete:" << _ptr << endl;delete _ptr;delete _pcount;}}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount){++(*_pcount);}shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (_ptr == sp._ptr)return *this;if (--(*_pcount) == 0){delete _ptr;delete _pcount;}_ptr = sp._ptr;_pcount = sp._pcount;++(*_pcount);return *this;}int use_count() const{return *_pcount;}T* get() const{return _ptr;}private:T* _ptr;int* _pcount;
};
7.3.4 weak_ptr
std::weak_ptr
weak_ptr是C++中为了配合shared_ptr解决循环引用问题
而引入的一种智能指针。
weak_ptr可以从一个shared_ptr或另一个weak_ptr对象构造,它的构造和析构不会引起shared_ptr引用记数的增加或减少。 weak_ptr的一个重要用途是通过lock获得this指针的shared_ptr,使对象自己能够生产shared_ptr来管理自己。总的来说,weak_ptr主要用于配合shared_ptr,以避免内存泄漏。
下面简化模拟实现了一份weak_ptr来了解它的原理:
template<class T>
class weak_ptr
{
public:weak_ptr():_ptr(nullptr){}weak_ptr(const shared_ptr<T>& sp):_ptr(sp.get()){}weak_ptr<T>& operator=(const shared_ptr<T>& sp){_ptr = sp.get();return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private:T* _ptr;
};
相关文章:

【C++】C++11——智能指针、内存泄漏、智能指针的使用和原理、RAII、auto_ptr、unique_ptr、shared_ptr、weak_ptr
文章目录 C117.智能指针7.1内存泄漏7.2智能指针的概念7.3智能指针的使用7.3.1 auto_ptr7.3.2 unique_ptr7.3.3 shared_ptr7.3.4 weak_ptr C11 7.智能指针 7.1内存泄漏 什么是内存泄漏: 内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏…...

EDUSRC-记某擎未授权与sql注入
目录 360天擎 - 未授权与sql注入 信息收集 FOFA语法 鹰图搜索 360天擎未授权访问 - 数据库信息泄露 漏洞复现 修复方案 360天擎终端安全管理系统ccid处SQL注入 漏洞复现 手动测试方法 修复方案 360天擎 - 未授权与sql注入 通常访问的页面如下,存在登录框…...

1688拍立淘API接口分享
拍立淘接口,顾名思义,就是通过图片搜索到相关商品列表。通过此接口,可以实现图片搜索爆款商品等功能。 接口地址:1688.item_search_img 公共参数 名称类型必须描述keyString是调用key(必须以GET方式拼接在URL中&…...
昇腾910使用记录
一. 压缩文件和解压文件 1. 压缩文件 tar -czvf UNITE-main.tar.gz ./UNITE-main/2. 解压文件 tar -xvf ./UNITE-main/二. CUDA更改为NPU data[label] data[label].cuda() data[instance] data[instance].cuda() data[image] data[image].cuda()更改为 data[label] da…...

从一部iPhone手机看芯片的分类
目录 问题 iPhone X 手机处理器:A11 iPhone X 的两大存储芯片 数字 IC CPU:计算设备的运算核心和控制核心 GPU:图形处理器 ASIC:为解决特定应用问题而定制设计的集成电路 存储芯片:DRAM 和 NAND Flash iPhone…...
arm day 7
完成字符串收发函数的封装并且验证现象,一个字符串发送接受后会有‘\n’ \r src/uart.c #include"uart.h"void uart4_init() {//设置UART4的RCc时钟使能//RCC_MP_APB1ENSETR[16]->1RCC->MP_APB1ENSETR | (0x1<<16);//设置GPIOB和GPIOG的时钟…...
Java基础面试-面向对象
什么是面向对象? 对比面向过程,是两种不同的处理问题角度 面向过程更注重事情的每一个步骤及顺序,面向对象更注重事情有哪些参与者(对象),及各自需要做什么 比如洗衣机洗衣服 面向过程会将任务拆解成一系…...
GCC vs. G++:C 与 C++ 编译器的差异和比较
本文将介绍 GCC(GNU Compiler Collection)和 G 编译器的区别,并对它们在 C 和 C 程序开发中的特性和用法进行比较和总结。 引言 在 C 和 C 程序开发中,选择合适的编译器是至关重要的。GCC(GNU Compiler Collection&a…...
MAC m系列docker login报错
错误:ERROR: failed to solve: XXX error getting credentials - err: exit status 1, out: 解决: vi ~/.docker/config.jsonzsxzsx [15时55分55秒] [~] { {"auths": {"harbor-g42c.corp.matrx.team": {"auth": "…...
Redis通用指令和五大基本数据类型常用指令总结
通用指令 keys parttern 查询key (parttern即通配符,不是正则表达式,例如 keys a? 匹配以a开头的长度为2的key) del key 删除key exists key 获取key是否存在 type key 获取key的类型 expire key seconds 为指定key设置有效期,单位秒 …...

uCharts常用图表组件demo
带渐变阴影的曲线图 <view class"charts-box"><qiun-data-charts type"area" :opts"opts" :chartData"chartData" :ontouch"true":background"rgba(256,256,256,0)" /> </view>data(){return{…...

VNC:Timed out waiting for a response from the computer
VNC的服务端使用的是TigerVNC,客户端使用的是RealVNC TigerVNC按其他博客配好后,防火墙ip什么的都配了,vnc客户端怎么连都是超时。 这里建议大家可以尝试一下重启服务器。我的是CentOS的 shutdown -r now 配了2天,最后服务器重启…...
Kotlin 协程 知识点
Android 上的 Kotlin 协程 | Android Developers (google.cn) 官方网址 1.什么是协程? 我觉得协程就是kotlin中一种优雅的实现异步请求 协程(Coroutines)是一种轻量级的并发编程概念,旨在简化异步编程和并发任务的处理。它是…...

简单大方的自我介绍 PPT 格式
自我介绍是展示自己的机会,同时也是展现自信和魅力的重要时刻。通过简单大方的PPT格式,可以更好地展示自己的个性和才华。下面是一些建议,帮助你在自我介绍中展现自信和魅力。 1. 打造简洁而有吸引力的PPT布局: - 选择简洁大方的背…...

panads操作excel
panads简介 pandas是基于Numpy创建的Python包,内置了大量标准函数,能够高效地解决数据分析数据处理和分析任务,pandas支持多种文件的操作,比如Excel,csv,json,txt 文件等,读取文件之…...
【MySQL】联合查询、子查询、合并查询
这里提供了三个表: 表1: mysql> select * from class; -------------- | id | name | -------------- | 1 | 一班 | | 2 | 二班 | | 3 | 三班 | -------------- 3 rows in set (0.01 sec) 表2: mysql> select * fro…...

小程序中如何设置所服务地区的时区
在全球化的背景下,小程序除了在中国使用外,还为海外的华人地区提供服务。例如我们采云小程序为泰国、阿根廷、缅甸等国家的商家就提供过微信小程序。这些商家开通小程序,为本地的华人提供服务。但通常小程序的开发者/服务商位于中国ÿ…...

Linux环境安装mysql8.0
1个人习惯我喜欢给软件安装在/use/local下,我使用的finalshell软件,直接手动新建一个文件夹名字为mysql 2下载mysql wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.20-linux-glibc2.12-x86_64.tar.xz 3解压文件 tar -xvf mysql-8.0.2…...
STM32_DMA_多通道采集ADC出现错位现象
STM32_DMA_多通道采集ADC出现错位现象 问题描述: adcSensorValue[0],adcSensorValue[3],adcSensorValue[6]… //存储通道1数据 adcSensorValue[1],adcSensorValue[4],adcSensorValue[7]… //存储通道2数据 adcSensorValue[2],adcSensorValue[5],adcSensorValue[8]……...

Linux内存管理 (2):memblock 子系统的建立
前一篇:Linux内存管理 (1):内核镜像映射临时页表的建立 文章目录 1. 前言2. 分析背景3. memblock 简介3.1 memblock 数据结构3.2 memblock 接口 4. memblock 的构建过程 1. 前言 限于作者能力水平,本文可能存在谬误,因此而给读者…...

linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...

华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...

AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...