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内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

使用Python实现可恢复式多线程下载器

《使用Python实现可恢复式多线程下载器》在数字时代,大文件下载已成为日常操作,本文将手把手教你用Python打造专业级下载器,实现断点续传,多线程加速,速度限制等功能,感兴趣的小伙伴可以了解下... 目录一、智能续传:从崩溃边缘抢救进度二、多线程加速:榨干网络带宽三、速度控制:做网络的好邻居四、终端交互

Python中注释使用方法举例详解

《Python中注释使用方法举例详解》在Python编程语言中注释是必不可少的一部分,它有助于提高代码的可读性和维护性,:本文主要介绍Python中注释使用方法的相关资料,需要的朋友可以参考下... 目录一、前言二、什么是注释?示例:三、单行注释语法:以 China编程# 开头,后面的内容为注释内容示例:示例:四

从入门到精通C++11 <chrono> 库特性

《从入门到精通C++11<chrono>库特性》chrono库是C++11中一个非常强大和实用的库,它为时间处理提供了丰富的功能和类型安全的接口,通过本文的介绍,我们了解了chrono库的基本概念... 目录一、引言1.1 为什么需要<chrono>库1.2<chrono>库的基本概念二、时间段(Durat

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性:

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

ModelMapper基本使用和常见场景示例详解

《ModelMapper基本使用和常见场景示例详解》ModelMapper是Java对象映射库,支持自动映射、自定义规则、集合转换及高级配置(如匹配策略、转换器),可集成SpringBoot,减少样板... 目录1. 添加依赖2. 基本用法示例:简单对象映射3. 自定义映射规则4. 集合映射5. 高级配置匹

Spring 框架之Springfox使用详解

《Spring框架之Springfox使用详解》Springfox是Spring框架的API文档工具,集成Swagger规范,自动生成文档并支持多语言/版本,模块化设计便于扩展,但存在版本兼容性、性... 目录核心功能工作原理模块化设计使用示例注意事项优缺点优点缺点总结适用场景建议总结Springfox 是

嵌入式数据库SQLite 3配置使用讲解

《嵌入式数据库SQLite3配置使用讲解》本文强调嵌入式项目中SQLite3数据库的重要性,因其零配置、轻量级、跨平台及事务处理特性,可保障数据溯源与责任明确,详细讲解安装配置、基础语法及SQLit... 目录0、惨痛教训1、SQLite3环境配置(1)、下载安装SQLite库(2)、解压下载的文件(3)、