C++学习笔记----6、内存管理(一)---- 使用动态内存(1)

2024-09-02 02:04

本文主要是介绍C++学习笔记----6、内存管理(一)---- 使用动态内存(1),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        当你使用现代结构,例如std::vector,std::string等等,从一开始到现在以及到未来,C++是一个安全的编程语言。该语言提供了许多的道路,路线以及红绿灯,比如C++核心指导,静态代码分析器来分析代码的正确性,等等。

        然而,C++依然允许你出轨。一个出轨的例子就是手动管理内存(分配与释放内存)。对于C++编程这种手动管理内存是一个特别容易出错的领域。为了写出高质量的C++程序,专业的C++程序员需要理解内存在底层是怎么工作的。前面的博文也介绍过一点儿,我们会继续讨论动态内存的陷阱以及避免以及消除它们的一些技巧。

        因为专业的C++程序员会碰到底层内存处理的代码,所以我们会讨论这一部分的内容。然而,在现代C++代码中,你应该尽量避免底层内存操作。例如,不要使用C风格的数组进行动态内存分配,要使用像vector这样的标准库容器,它们会为你自动处理所有的内存管理。不要用原始的指针,要用智能指针,比如unique_ptr和shared_ptr,后面我们慢慢讨论,它们会自动释放分配的资源,像不再需要的内存等。本质上来说,就是不要再去调用像new/new[]和delete/delete[]这样的内存分配函数了。当然,这不总是可以的,在原有的代码中,可能不是这样,所以做为一个专业的C++程序员,你依然需要知道内存在底层是如何工作的。你可以不使用这些底层的操作,但不代表你可以不明白。

        在现代C++代码中,应该尽可能地避免进行内存底层操作,当牵涉到属主的时候避免原始指针,避免使用旧的C风格的结构与函数。反过来,要使用安全的C++替代方法,例如自动管理内存的对象,像C++的string类,vector容器,智能指针等等。

        好了,言归正传吧,我们先来讨论一下动态内存的使用。

        内存是一个底层的部件 ,对于计算机来说,有时候很不幸地成为像C++这样的高级编程语言来说也是一个不祥之物。当然了,真正理解了动态内存是如何工作的对于成长为一个专业的C++程序员非常重要。

1、如何展示内存

        如何 你有一个内存对象的在大脑当中的模型的话,那理解起动态内存来就会容易得多了。我们的表示方法就是在一个方框旁边一个标记。这个标记就是一个对这个内存对应的名字。方框内的数据就是这个内存的当前值。

        举个例子,下图显示了以下代码执行为的内存状态。这段代码应该在一个函数中,所以变量是一个局部变量。

int i { 7 };

        i就是一个在栈上分配了空间的自动变量。当程序流离开它声明的范围时会自动释放。

        当你使用new关键字时,内存就会在自由空间内存上进行分配。如果没有显式初始化,通过对new的调用分配的内存就没有初始化;也就是说,内存空间的值是任何可能的随机值。我们会以一个?来代表这种没有初始化的状态。以下代码生成了一个ptr的变量,在栈上用nullptr进行了初始化,然后在自由空间内存上给ptr指针分配了内存:

int* ptr { nullptr };
ptr = new int;

          也可以用一行代码来实现:  

int* ptr { new int };

        下图展示了代码执行后的内存状态。

        注意变量ptr仍然在栈上,即使它指向了自由内存空间。指针只是一个变量,可以在栈上或自由内存空间存在。虽然这个事实很容易被遗忘。然而,动态内存总是在自由内存空间进行分配。

        提醒一下,C++核心指导指出,每一次声明指针变量的时候,都要立即用合适的指针或者nullptr进行初始化。不要留后患。

        下面的例子展示了指针可以在栈上也可以在自由内存空间存在。

int** handle { nullptr };
handle = new int*;
*handle = new int;

        这段代码首先声明了一个指向指向整数的指针变量handle。它就动态地分配的足够的内存来保存一个指向整数的指针,在handle中保存指向新内存的指针。然后,内存(*handle)被分配给了一个指向另一块足够保存整数的动态内存片的指针。下图展示 了两层指针,一个指针位于栈上(handle),另一个位于自由内存空间(*handle)。

2、分配与释放内存

        为变量分配空间,需要作用use关键字。释放该空间可以让程序的其他部分使用,需要使用delete关键字。

2.1、使用use和delete

        当你想要分配一块儿内存,你需要调用带有该类型变量需要的空间的new关键字。new返回一个指向该内存的指针,保存这个指针变量就靠你了。如果你忽略掉new的返回值或者指针变量越界,内存就会变成孤儿,因为无法再访问到它。这就叫做内存泄露。

        例如,下面的代码就使用保存int的内存变成了孤儿。下图展示了代码执行后的内存状态。

void leaky()
{new int; // BUG! 内存泄露!println("I just leaked an int!");
}

        当有大块的数据在自由内存空间无法访问,从栈上不管是直接访问或者间接访问都无法做到,那么这块内存就是孤儿或者内存泄露了。

        在找到让计算机无限地供给内存之前,需要告诉编译器当与之相连的对象可以被释放用于其他目的。为了释放自由内存空间上的内存,需要使用delete关键字后跟指向内存的指针,如下代码所示:

int* ptr { new int };
delete ptr;
ptr = nullptr;

        从经验上来讲,每一行用new分配内存的代码,使用原始指针而不是使用智能指针保存指针的代码,都应该有对应的使用delete的一行代码来释放同样的内存。

        推荐在释放了内存之后,将指针赋一个nullptr的值。这样的话,就可以避免使用已经被释放了内存的指针。也需要指出的是,对于nullptr指针调用delete也是被允许的,只不过它啥也不做而已。

这篇关于C++学习笔记----6、内存管理(一)---- 使用动态内存(1)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1128685

相关文章

Java使用ANTLR4对Lua脚本语法校验详解

《Java使用ANTLR4对Lua脚本语法校验详解》ANTLR是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件,下面就跟随小编一起看看Java如何使用ANTLR4对Lua脚本... 目录什么是ANTLR?第一个例子ANTLR4 的工作流程Lua脚本语法校验准备一个Lua Gramm

Java Optional的使用技巧与最佳实践

《JavaOptional的使用技巧与最佳实践》在Java中,Optional是用于优雅处理null的容器类,其核心目标是显式提醒开发者处理空值场景,避免NullPointerExce... 目录一、Optional 的核心用途二、使用技巧与最佳实践三、常见误区与反模式四、替代方案与扩展五、总结在 Java

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

Qt中QUndoView控件的具体使用

《Qt中QUndoView控件的具体使用》QUndoView是Qt框架中用于可视化显示QUndoStack内容的控件,本文主要介绍了Qt中QUndoView控件的具体使用,具有一定的参考价值,感兴趣的... 目录引言一、QUndoView 的用途二、工作原理三、 如何与 QUnDOStack 配合使用四、自

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

使用Python构建一个Hexo博客发布工具

《使用Python构建一个Hexo博客发布工具》虽然Hexo的命令行工具非常强大,但对于日常的博客撰写和发布过程,我总觉得缺少一个直观的图形界面来简化操作,下面我们就来看看如何使用Python构建一个... 目录引言Hexo博客系统简介设计需求技术选择代码实现主框架界面设计核心功能实现1. 发布文章2. 加

在Spring Boot中浅尝内存泄漏的实战记录

《在SpringBoot中浅尝内存泄漏的实战记录》本文给大家分享在SpringBoot中浅尝内存泄漏的实战记录,结合实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录使用静态集合持有对象引用,阻止GC回收关键点:可执行代码:验证:1,运行程序(启动时添加JVM参数限制堆大小):2,访问 htt

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

python uv包管理小结

《pythonuv包管理小结》uv是一个高性能的Python包管理工具,它不仅能够高效地处理包管理和依赖解析,还提供了对Python版本管理的支持,本文主要介绍了pythonuv包管理小结,具有一... 目录安装 uv使用 uv 管理 python 版本安装指定版本的 Python查看已安装的 Python

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的