C++——内存管理(new/delete使用详解)

C++内存管理

本章思维导图:
在这里插入图片描述注:本章思维导图对应的xmind文件和.png文件已同步导入至资源

1. C/C++内存区域的划分

在C/C++中,内存区域主要划分为:内核区域、栈区、内存映射段、堆区、数据段、代码段等区域,如图:

在这里插入图片描述

本篇我们主要讨论栈区、堆区、数据段和代码段这四个区域。

栈区:主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。存放在栈区的变量的生命周期就是所在函数的作用域

堆区:主要存放的是malloc、realloc、calloc、new等动态开辟函数开辟的空间。存放在堆区的变量的生命周期是整个进程

数据段:主要存放的是我们在函数外定义的全局数据,或者static静态数据。存放在数据段的变量的生周期是整个进程

代码段:主要存放的是const只读常量即字面量

下面,我们通过一个具体的例子来弄清楚各类数据到底存放在计算机的那一块空间:

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

Question:

/*  
选项: A.栈  B.堆  C.数据段(静态区)  D.代码段(常量区)globalVar在哪里?____   staticGlobalVar在哪里?____   staticVar在哪里?____   const localVar在哪里?____num1 在哪里?____char2在哪里?____   *char2在哪里?___pChar3在哪里?____      *pChar3在哪里?____ptr1在哪里?____        *ptr1在哪里?____
*/
  • globalVar定义在全局(函数外),是一个全局变量,因此存放在数据段
  • staticGlobalVar也是一个定义在全局的静态变量,因此也存放在数据段(静态区)
    • 需要注意:尽管globalVarstaticGlobalVar都存放在数据段中,但是它们之间也有链接属性的区别。globaVar可以多文件共同使用,而staticGlobalVar只能在本文件使用。
  • staticVar尽管定义在函数内,但是由static修饰,是一个静态变量,因此也存放在数据段
  • const localVar定义在函数内,是一个局部变量,因此存放在栈区
    • 注:千万不要认为localVarconst修饰他就存放在代码段(常量区),const只是修饰局部变量localVar,表示它的值不能被修改。
  • num1是静态开辟的数组,所以存放在栈区
  • char2也是个静态开辟的字符串数组,因此也存放在栈区

  • *char2:这里的char2表示首元素的地址,所以*char2就表示字符a。看到这里,可能有小伙伴就会说*char2存放在常量区,但事实上*char2还是存放在栈区。我们可以从两个方面解释:

    • 常量区的数据不能被修改但是对于*char2,我们可以对其进行修改,也就是普通的堆字符数组进行修改,因此*char2不在常量区,而是在栈区。

    • 实际上,初始化字符数组char2的字符abcd是存放在常量区的字符abcd的拷贝,这份拷贝同样存放在栈区:

      在这里插入图片描述

  • pchar3就是定义在函数内的局部变量,因此存放在栈区

  • pchar3指针指向的就是字符串字面量abcd,而字符串字面量const常量,因此存储在代码段

  • ptr1就是定义在函数内的局部变量,因此存放在栈区

  • ptr1指向的是malloc开辟的空间,因此*ptr1存放在堆区

2. new/delete

在这里插入图片描述

  • C++使用newdelete动态管理内存
  • newdelete是C++内置的两个运算符,不需要显示的包含头文件来使用。

2.1 new/delete和malloc/free的区别

有小伙伴可能会有疑惑:

既然C语言已经可以用malloc、realloc、free等函数来动态管理内存了,为什么C++还要新创建两个运算符newdelete来替代C语言的方法呢?

一个原因是,C++是面向对象的,C++有C语言没有的class类,类的初始化必须调用构造函数,但是构造函数不能显示调用,例如:

class A
{
public:A(int a = 1):_a(a){}private:int _a;
};int main()
{A* p = (A*)malloc(sizeof(A));p->A();return 0;
}//会报错:类型名称“A”不能出现在类成员访问表达式的右侧

可见,C语言并不能解决C++自定义类型初始化的问题,所以C++才要新创建两个运算符newdelete来解决这一问题。

这次,我们换用C++的new运算符来动态开辟内存,并用delete进行空间的释放:

class A
{
public:A(int a = 1):_a(a){}~A(){}private:int _a;
};int main()
{A* p = new A;delete p;return 0;
}

让我们进行调试:

在这里插入图片描述

可以看到:

  • new在给自定义类型开辟空间时,会在给对象开辟完空间后继续调用该对象的默认构造完成初始化

  • delete在释放自定义类型对象之前,会先调用对象的析构函数,再释放对象的空间

除了上面最重要的不同之外,malloc/freenew/delete还有许多不同之处:

  • malloc/free是函数,而new/delete是运算符
  • malloc开辟空间时需要用sizeof计算开辟空间的大小,而new只需要在后面加上开辟对象的类型就行
  • malloc的返回值时void*,实际使用时需要强制类型转换;而new不需要,因为开辟时就指定了对象类型
  • malloc失败时,会返回0,因此需要对结果进行判空;而new不需要,但是需要进行异常捕获

2.2 new/delete的使用

如果一次只开辟、释放单个对象,基本格式为:

  • new type;
  • 例如:int*p = new int; delete p;

如果一次开辟、释放多个对象,基本格式为:

  • new type[nums]
  • 例如:int*p = new int[10]; delete[] p

对于自定义类型,newdelete的使用和内置类型基本一致,但是我们可以在new的同时给默认构造传参(如果可以传的话),这样可以一次创建初始值不同的多个同一类型的多个对象:

例如:

class AB
{
public:AB(int a = 1, int b = 1): _a(a), _b(b){}
private:int _a;int _b;
};class C
{
public:C(int c = 1):_c(c){}
private:int _c;
};int main()
{AB* p1 = new AB;	//生成一个对象:_a = 1, _b =  1AB* p2 = new AB{ 2,2 };		//生成一个对象: _a = 2, _b =  2C* p3 = new C(2);	//生成一个对象:_c = 2C* p4 = new C[3];	//生成三个对象:_c都为1C* p5 = new C[3]{ 2, 3, 4 };	//生成三个对象:_c分别为2,3,4AB* p6 = new AB[3]{ {2,2}, {3,3}, {4,4} };	//生成三个对象: _a, _b分别为(2,2)、(3,3)、(4,4)delete p1;delete p2;delete p3;delete[] p4;delete[] p5;delete[] p6;return 0;
}

特别注意

new/deletenew[]/delete[]必须配套使用,不能随意搭配!!!

2.3 new/delete的底层原理

在这里插入图片描述

实际上,执行运算符new开辟空间和delete释放空间时,编译器都会调用operator newoperaotr delete这两个函数

注意:

不要operator newoperaotr delete这两个函数理解为运算符重载

我们可以先来看看operator new的具体实现:

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)if (_callnewh(size) == 0){// report no memory// 如果申请内存失败了,这里会抛出bad_alloc 类型异常static const std::bad_alloc nomem;_RAISE(nomem);}
return (p);
}

可以看到,operator new开空间时实际上用的也是C语言的malloc函数,operator new只不过是对malloc的一层封装。那么为什么要对malloc进行封装呢

malloc失败时返回的是整数0,这不符合C++面向对象的要求,因此C++要对malloc进行封装,使内存开辟失败时可以抛出异常

同样**,实际上operator delete也是用C语言的free函数,来实现对空间的释放**

//等效
int* p1 = (int*)malloc(sizeof(int) * 10);
int* p2 = (int*)operator new(sizeof(int) * 10);//等效
free(p1);
operator delete(p2);

现在,我们对于C++newdelete对于自定义类型空间的开辟和释放就更加明了了:

  • new首先调用operator new来开辟对象的空间,再调用对象的默认构造进行初始化
  • delete首先调用析构函数释放对象成员变量的资源,再调用operator delete释放该对象的空间

2.4 定位new(仅作了解)

在这里插入图片描述

在之后的学习过程中,我们可能会遇到需要显式调用自定义类型构造函数的情况。但是一般情况下,构造函数不支持显式调用,此时定位new就可以帮我们解决这个问题。

  • 定位nwe可以显示调用构造函数
  • 其基本格式为new (place_address) type或者new (place_address) type(initializer-list)
  • place_address就是是一个指向和一个类相同大小空间的指针
  • type就是类类型
  • initializer-list就是构造函数的参数列表

例如:

class A
{
public:A(int a = 1): _a(a){}~A(){}
private:int _a;
};//构造函数不能显式调用,但是析构函数可以
int main()
{A* p = (A*)malloc(sizeof(A));new(p)A(10);p->~A();return 0;
}![请添加图片描述](https://img-blog.csdnimg.cn/49bacd2462d14cf3a6b58056d5206c4a.gif)

下一篇,我们将对C++的模板和泛型编程展开讲解,感兴趣的小伙伴可以关注此专栏。

请添加图片描述

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

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

相关文章

在搭建企业知识库时,这三个重要方面可不能忽略

随着信息时代的到来,企业面临着海量的信息和知识的挑战。为了更好地组织、管理和利用企业内部的知识资源,越来越多的企业开始搭建自己的知识库系统。 企业知识库是一个集中存储和管理知识、经验和信息的平台,它不仅可以提高企业的协作效率&a…

mfc140u.dll丢失怎么修复?4种亲测有效的方法分享

在计算机使用过程中,我们可能会遇到各种各样的问题,其中之一就是某些重要的dll文件丢失。DLL文件是动态链接库文件,它们包含了许多程序运行所需的函数和资源。当这些文件丢失或损坏时,可能会导致程序无法正常运行。本文将详细介绍…

5+单基因+免疫浸润,这篇肿瘤预后文章你值得拥有

今天给同学们分享一篇生信文章“Systematic analysis of the role of SLC52A2 in multiple human cancers”,这篇文章发表在Cancer Cell Int期刊上,影响因子为5.8。 结果解读: 多种人类癌症中SLC52A2的mRNA表达 首先,作者使用GT…

AI由许多不同的技术组成,其中一些最核心的技术如下

AI由许多不同的技术组成,其中一些最核心的技术包括: 机器学习:这是一种让计算机从数据中学习的技术,它可以根据已有的数据预测未来的趋势和行为。机器学习包括监督学习、无监督学习和强化学习等多种类型。深度学习:这…

零成本体验美国云服务器,更方便的体验和选择

在当今数字化时代,云计算已经成为了企业和个人的首选。而美国云服务器免费试用,则为广大用户提供了一个零风险尝试的机会。作为一种高效、灵活、稳定的解决方案,美国云服务器可以为您的业务保驾护航。 什么是美国云服务器? 美国云…

Python自动化测试:web自动化测试——Selenium框架

web自动化测试1 Selenium介绍web自动化实现原理环境准备1)Seleniumpython环境搭建安装步骤环境变量的配置 2)浏览器驱动驱动下载驱动环境配置 3)版本检查4)其他异常情况排查版本不一致未激活卸载、降低/升级setuptools版本 web自动…

Python框架篇(1):FastApi-快速入门

1.介绍 前言: 不管学什么语言,都应该至少掌握一个框架,方面我们后续,进行服务部署、服务对外支持等; 1.1 官网介绍 下面是来自FastAPI官网的介绍: FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框架&#…

找不同游戏-第15届蓝桥第二次STEMA测评Scratch真题精选

[导读]:超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成,后续会不定期解读蓝桥杯真题,这是Scratch蓝桥杯真题解析第157讲。 第15届蓝桥杯第2次STEMA测评已于2023年10月29日落下帷幕,编程题一共有6题,分别如下&…

阿里云服务器租用价格,不同实例云服务器日常价、活动价与券后价格

阿里云服务器最新实际购买价格参考,轻量应用服务器2核2G3M带宽配置日常价720.00元/1年,最新活动价格为87元/1年,订单满300元以上即可使用满减优惠券,例如经济型e实例2核4G2M带宽日常价格为1802.40元,最新的活动价格为8…

小户型的创新之选:嵌入式酒精壁炉

小户型中的热需求与大房子并无不同,但常规暖气设备往往难以适应有限的空间。此时,嵌入式酒精壁炉崭露头角。它们不仅提供了足够的温暖,还具备了独特的美感,无需占用大面积的地方。 嵌入式酒精壁炉的精妙之处在于它们出色的空间利用…

Centos8配置Zabbix5.0中文汉化

1.点击【Sign in】按钮,输入用户名和密码进入Zabbix的首页,结果如图。 2.点击左边导航栏的【User settings】链接,进入用户个性化设置界面,结果如图。 3.在搭建Zabbix的虚拟机上使用yum命令下载中文包。 yum install glibc-langpa…

算法学习打卡day45|动态规划:股票问题总结

Leetcode股票问题总结篇 动态规划的股票问题一共六道题,买卖股票最佳时机和买卖股票手续费都是一个类型的问题,维护好买入和卖出两个状态即可,方法一摸一样。而冷冻期也差不多就是状态多了点,买入、保持卖出、当日卖出、以及冷冻期…