C++(9)——内存管理

   

1. 内存分类:

    在前面的文章中,通常会涉及到几个名词,例如:栈、堆。这两个词所代表的便是计算机内存的一部分 。在计算机中,对系统的内存按照不同的使用需求进行了区分,大致可以分为:栈 、堆、数据段、代码段。其各个具体解释如下:
      1. 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。
      2. 堆用于程序运行时动态内存分配,堆是可以上增长的。
      3. 数据段(静态区)--存储全局数据和静态数据。
      4. 代码段(常量区)--可执行的代码/只读常量。

为了更加深入的了解各个内存空间之间的差异,下面给出一个例子:

int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{static int staticVar = 1;int localVar = 1;int num1[10] = { 1, 2, 3, 4 };char char2[] = "abcd";const char* pChar3 = "abcd";int* ptr1 = (int*)malloc(sizeof(int) * 4);int* ptr2 = (int*)calloc(4, sizeof(int));int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);free(ptr1);free(ptr3);

1. 对于变量globalVar,是一个全局变量,存储在数据段(静态区)。

2.对于变量staticGlobalVar,在定义的时候加上了关键字static,存放在数据段(静态区)。

前两个变量虽然都存储在数据段(静态区),但是二者的链接属性并不相同,对于globalVar,可以在同一工程下的不同文件中共享使用。但是对于staticGlobalVar,只能在本文件中使用。

3. 对于变量staticVar,也是一个被关键字static修饰的静态变量,同样存储在数据段(静态区),但是只能在本函数内部使用。

4.对于变量localVar,是函数内部的一个临时变量,存储在栈区

5.对于变量num1,是一个函数内部的数组,存储在栈区

6.对于变量char2,与变量num1同理,都是存储在

	int* n1 = new int;

栈区上的

7.对于变量*char2,与num1类似,存储在栈区上

8.对于变量pchar3,需要注意,在定义这个变量时,前面的const修饰的并不是pchar3,而是*pchar3。因此,pchar3也是存储在栈区,但是需要注意,*pchar3,即字符串abcd则是存储在代码段(常量区)。

9.对于ptr1,与pchar3类似,也是存储在栈区上的,但是*ptr1,即后面利用malloc开辟的空间是在堆区的。因此*ptr1是存储在堆区上的

2. C++中的内存管理:

2.1 开辟内存空间:

       在C语言中,对于内存的管理通常是使用malloc,realloc,free等函数完成的,在数据结构中,经常在创建一个关于数据结构的单个结点时使用。在C++中,引入了两个关键字new,delete来实现内存管理。其中,new用来开辟内存空间,delete用来释放开辟的内存空间。对于这两个操作符的使用,下面将引入若干例子来说明:
       

       1. 开辟一个类型为int的空间:

int* n1 = new int;

      2.一次性开辟10个类型为int的空间:

int* n1 = new int[10];

     在C语言——动态内存管理:_编写内存管理代码-CSDN博客中提到,对于函数malloc,只能开辟空间,并不能对开辟的空间进行初始化。而函数calloc可以在开辟空间的同时将所开辟的空间初始化为0。对于关键字new,在上面的使用方法中,同样无法完成对于开辟空间的初始化。如果想初始化开辟的空间,例如将开辟的一个类型为int的空间初始化为0,则:
 

int* n2 = new int(0);  //开辟一个类型为Int的空间,初始化为0

运行结果如下:

若需要同时初始化多个空间,则:

int* n3 = new int[10]{ 1,2,3 }; //开辟10个类型为int的空间,并初始化其中三个空间为1,2,3

 运行结果如下:

       不难发现,在上述代码中,开辟了10个空间,但是仅对其中三个进行了初始化。对于其他的空间,默认初始化为0

2.2 释放内存空间:

   针对使用关键字new开辟的内存空间,需要用关键字delete进行删除。对于单个空间的释放,代码如下:

int* n = new int;      //开辟一个空间,类型为intdelete n; //释放上方开辟的空间

对于多个空间的释放,代码如下:
 

int* n1 = new int[10]; //开辟10个类型为int的空间delete[]n1;   //释放上面开辟的10个空间

3. 为什么要引入这两个关键字:

    前面说到,new,delete这两个关键字是在C++中引入的,虽然C语言中的函数malloc,calloc,realloc,free已经满足了对于内存的开辟及释放。但是,这些函数针对于自定义类型并不能解决初始化的问题。因此,需要引入newdelete来完成开辟自定义类型空间时的初始化问题。例如对于下方给出的自定义类型:

class A
{
public:A(int a = 0):_a(a){cout << " A() " << " " << this << endl;}~A(){cout << " ~A() " << " " << this << endl;}
private:int _a;
};

按照上方new开辟空间的方式,来开辟一个类型为A的空间,代码为:

A* n4 = new A;  //开辟一个类型为A的空间

       而针对于new如何对开辟的空间进行初始化。可以认为,在调用关键字new开辟空间时,关键字进行了两步:第一步是开辟空间,第二步则是调用自定义类型的默认构造函数(全缺省、不需要传参就可以调用、编译器自动生成),对于上述代码中的类,由于其构造函数中的参数为全缺省,因此可以看作一个默认构造函数,但运行上方代码时,结果如下:

   通过结果可以得知,在开辟空间时调用了自定义类型A的默认构造函数。 

如果想要显式调用,即不适用类中的构造函数的参数,方法如下:

A* n5 = new A(1);

类中成员变量如下:

上述开辟自定义类型空间只是开辟了一个,对于开辟多个自定义类型的空间,代码如下:

A* n6 = new A[5];

运行结果如下:

 对于开辟多个空间的初始化,有下面几种方法:

A a1(1);A a2(2);A a3(3);A* n7 = new A[3]{ a1,a2,a3 };

 同时,可以借助匿名对象来达成初始化的效果:

A* n8 = new A[3]{A(1), A(2), A(3)};

此外,如果自定义类型的构造函数满足单参数这一条件

(注:单参数并不准确,具体条件可以在C++类与对象基础(8)-CSDN博客进行查看)

可以借助隐式类型转换来完成初始化,即:

A* n9 = new A[3]{ 4,5,6 };

对于自定义类型空间的释放,代码如下:

	cout << "测试自定义类型空间的释放" << endl;A* n9 = new A[3]{ 4,5,6 };delete[]n9;

运行结果如下:

不难发现,代码在运行中调用了析构函数。原因是对于关键字delete,其动作过程也可以分为两步: 调用自定义类型的析构函数、释放空间。

4. 勘误:

  由于个人能力有限,书中难免出现汉字拼写错误、代码意义解释错误、内容逻辑以及理解错误等不同类型的错误。首先感谢各位大佬能花掉自己宝贵的时间阅读此文章,愿大佬们斧正,发现错误可以通过私信联系,本人不胜感激。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/343521.html

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

相关文章

解决uni-app小程序获取路由及路由参数

代码: this.id = this.$route.query.id;错误信息: 解决方案: // 获取query对象// #ifdef H5this.id = this.$route

OpenGl 19高级GLSL

一.GLSL的内建变量 在着色器中&#xff0c;需要当前着色器以外地方的数据的话&#xff0c;必须把数据传进来。之前我们是通过uniform类型和采样器来完成的。之外&#xff0c;GLSL还支持另外几个以gl为前缀的变量&#xff0c;提供更多读写数据的方式&#xff0c;比如说顶点着色…

第一次在RUST官方论坛上留言发布我的Rust板箱

第一次在RUST官方论坛上发帖子&#xff0c;有点紧张~地址在这里&#xff1a; 【My Rust Crate】obtains linux local information - The Rust Programming Language Forum (rust-lang.org)

C++——STL标准模板库——容器详解——list

一、基本概念 &#xff08;一&#xff09;定义 list&#xff1a;双向链表。list是一种分布式存储的线性表&#xff0c;每个节点分为数据域和指针域&#xff0c;其中指针域中包含一个指向前驱节点的指针和一个指向后续节点的指针&#xff0c;基本模型如下&#xff1a; &#…

Python学习从0到1 day2 python注释

那就祝我们 all is well ——24.1.12 一、python中的注释 1.注释的作用 注释代码与非注释代码对比&#xff0c;可以发现&#xff0c;添加了注释的代码更加易于程序的阅读 2.注释的分类及方法 注释在python中有两种形式&#xff1a;单行注释和多行注释。 单行注释只能注释一行…

日常工作中,软件测试人员如何避免“背锅”

作为一名软件测试工程师&#xff0c;日常工作中最常打交道的肯定就是开发和产品经理。有沟通就会问题&#xff0c;有问题难免会有争执。那么你肯定听过这些话&#xff1a; “这么弱智的bug你都测不出来吗&#xff1f;” “为啥这个功能还没测完就上线了&#xff1f;” “研发…

c++类程序设计题1

#include<iostream> #include<string> using namespace std;class cube{public ://设置长void setM(int m){M_l m;}int getl(){return M_l;}//设置宽void setr(int r){M_r r;}int get(){return M_r;}//设置高void setm(int m){M_m m;}int getm(){return M_m;}//…

Pycharm close project 速度缓慢解决办法

解决Pycharm close project缓慢现象 1.问题描述 close project后需要等待很长的时间。 2.解决办法 在Help -> Find Action -> 输入 Registry -> 禁用ide.await.scope.completion 问题解决&#xff01;&#xff01;&#xff01; &#x1f603;&#x1f603;&#x…

spring Security源码讲解-Sevlet过滤器调用springSecurty过滤器的流程

承接上文 上一节 http://t.csdnimg.cn/ueSAl 最后讲到了过滤器收集完成注入容器&#xff0c;这节我们来讲Security的Filter是怎么被Spring调用的。 我们先看webSecurity的performBuild方法(), 也就是说&#xff0c;最终返回的过滤器对象实例有两种情况当我们配置debugEnabl…

便捷特惠的快递寄件快递物流折扣平台 ,通常都有什么常见问题?

首先&#xff0c;最重要的一点是怎么寄快递更便宜&#xff1f; 我们在寄快递时&#xff0c;尽量把包裹压缩空间大一点&#xff0c;这样在体积上面就会减少一部分的费用呢&#xff0c;另外就是选择有优惠的平台下单。例如在闪侠惠递平台下单&#xff0c;单单打折&#xff0c;单…

深入理解 Flink(八)Flink Task 部署初始化和启动详解

JobMaster 部署 Task 核心入口&#xff1a; JobMaster.onStart();部署 Task 链条&#xff1a;JobMaster --> DefaultScheduler --> SchedulingStrategy --> ExecutionVertex --> Execution --> RPC请求 --> TaskExecutor TaskExecutor 处理 JobMaster 的 …

数据库系统概念 第七版 中文答案 第3章 SQL介绍

3.1 将以下查询使用SQL语言编写&#xff0c;使用大学数据库模式。 &#xff08;我们建议您实际在数据库上运行这些查询&#xff0c;使用我们在书籍网站db-book.com上提供的示例数据。有关设置数据库和加载示例数据的说明&#xff0c;请参阅上述网站。&#xff09; a. 查找计算机…