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使用Mail构建邮件功能的完整指南

《Java使用Mail构建邮件功能的完整指南》JavaMailAPI是一个功能强大的工具,它可以帮助开发者轻松实现邮件的发送与接收功能,本文将介绍如何使用JavaMail发送和接收邮件,希望对大家有所... 目录1、简述2、主要特点3、发送样例3.1 发送纯文本邮件3.2 发送 html 邮件3.3 发送带

Win32下C++实现快速获取硬盘分区信息

《Win32下C++实现快速获取硬盘分区信息》这篇文章主要为大家详细介绍了Win32下C++如何实现快速获取硬盘分区信息,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 实现代码CDiskDriveUtils.h#pragma once #include <wtypesbase

使用DeepSeek搭建个人知识库(在笔记本电脑上)

《使用DeepSeek搭建个人知识库(在笔记本电脑上)》本文介绍了如何在笔记本电脑上使用DeepSeek和开源工具搭建个人知识库,通过安装DeepSeek和RAGFlow,并使用CherryStudi... 目录部署环境软件清单安装DeepSeek安装Cherry Studio安装RAGFlow设置知识库总

Python FastAPI入门安装使用

《PythonFastAPI入门安装使用》FastAPI是一个现代、快速的PythonWeb框架,用于构建API,它基于Python3.6+的类型提示特性,使得代码更加简洁且易于绶护,这篇文章主要介... 目录第一节:FastAPI入门一、FastAPI框架介绍什么是ASGI服务(WSGI)二、FastAP

Spring-AOP-ProceedingJoinPoint的使用详解

《Spring-AOP-ProceedingJoinPoint的使用详解》:本文主要介绍Spring-AOP-ProceedingJoinPoint的使用方式,具有很好的参考价值,希望对大家有所帮... 目录ProceedingJoinPoijsnt简介获取环绕通知方法的相关信息1.proceed()2.g

Maven pom.xml文件中build,plugin标签的使用小结

《Mavenpom.xml文件中build,plugin标签的使用小结》本文主要介绍了Mavenpom.xml文件中build,plugin标签的使用小结,文中通过示例代码介绍的非常详细,对大家的学... 目录<build> 标签Plugins插件<build> 标签<build> 标签是 pom.XML

JAVA虚拟机中 -D, -X, -XX ,-server参数使用

《JAVA虚拟机中-D,-X,-XX,-server参数使用》本文主要介绍了JAVA虚拟机中-D,-X,-XX,-server参数使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有... 目录一、-D参数二、-X参数三、-XX参数总结:在Java开发过程中,对Java虚拟机(JVM)的启动参数进

Java中使用注解校验手机号格式的详细指南

《Java中使用注解校验手机号格式的详细指南》在现代的Web应用开发中,数据校验是一个非常重要的环节,本文将详细介绍如何在Java中使用注解对手机号格式进行校验,感兴趣的小伙伴可以了解下... 目录1. 引言2. 数据校验的重要性3. Java中的数据校验框架4. 使用注解校验手机号格式4.1 @NotBl

Python使用DeepSeek进行联网搜索功能详解

《Python使用DeepSeek进行联网搜索功能详解》Python作为一种非常流行的编程语言,结合DeepSeek这一高性能的深度学习工具包,可以方便地处理各种深度学习任务,本文将介绍一下如何使用P... 目录一、环境准备与依赖安装二、DeepSeek简介三、联网搜索与数据集准备四、实践示例:图像分类1.

Redis实现RBAC权限管理

《Redis实现RBAC权限管理》本文主要介绍了Redis实现RBAC权限管理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1. 什么是 RBAC?2. 为什么使用 Redis 实现 RBAC?3. 设计 RBAC 数据结构