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

news2024/11/25 19:44:00

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 =  1
	AB* p2 = new AB{ 2,2 };		//生成一个对象: _a = 2, _b =  2
	C* p3 = new C(2);	//生成一个对象:_c = 2

	C* p4 = new C[3];	//生成三个对象:_c都为1
	C* p5 = new C[3]{ 2, 3, 4 };	//生成三个对象:_c分别为2,3,4
	AB* 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.coloradmin.cn/o/1208408.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mysql数据库报错:1166-Incorrect column name ‘xxx‘

如图,我的报错是:1166-Incorrect column name ‘book_date’,很奇怪,其它的字段都没有报错,但是book_date报错了 报错原因:引入了空字符 可以看到我的鼠标和book_date中间还有一个空格。所以导致该行创建失…

ERP是什么意思?看这一篇就够了!

如果你身在制造业,那么一定对ERP不陌生。天天把ERP挂在嘴边,但你真的了解什么是ERP吗?本篇文章将介绍以下几点:1.ERP是什么意思;2.ERP的功能;3.ERP的落地案例。 一、ERP是什么意思 ERP是企业资源计划&…

5、鸿蒙项目远程调试

一、注册华为账号, 如果是华为手机,并注册了账号可能跳过此步骤,如果使用邮箱注册,此邮箱一定是要正确的邮箱,此处需要使用邮箱获取验证码 注册地址:‎ 1、进入注册页面,输入手机号等信息后点…

Python 爬虫之scrapy 库

文章目录 总的介绍相关模块 总的介绍 Scrapy是一个用于爬取网站数据的开源Python框架。它提供了一套强大而灵活的工具,用于从网站上提取所需的数据。Scrapy是基于Twisted异步网络库构建的,因此可以高效地处理大量的并发请求。以下是Scrapy的一些主要特点…

APUS与深圳大学大数据国家工程实验室联合训练开源中文大模型

日前,APUS与深圳大学大数据系统计算技术国家工程实验室(以下简称“国家工程实验室”)达成战略合作。双方集成各自优势联合开发、开源高性能中文多模态大模型Linly-Chinese-LLaMA-2-70B。该模型将更加适配中文服务场景,计划于2024年…

WebDAV之π-Disk派盘 + RS文件管理器

手机本地文件,网盘、共享文件,如何集中管理?推荐您使用Rs文件管理器,还支持WebDAV等功能。 Rs文件管理器是一款功能强大的手机文件管理器。有强大的本地和网络文件管理功能,让您更方便的管理你的手机、平板、电脑和网盘。可以帮助您轻松管理手机本地存储文件和网络文件,…

keepalived+haproxy配置集群和负载均衡

1、简介 1.1. Keepalived Keepalived 是一个基于VRRP协议来实现的LVS服务高可用方案,可以利用其来避免单点故障。一个LVS服务会有2台服务器运行Keepalived,一台为主服务器(MASTER),一台为备份服务器(BACKUP),但是对外表现为一个虚拟IP,主服务器会发送特定的消息给备…

19C进入数据库出现问号

问题情况如图所示: 解决方法: su - oracle echo "NLS_LANGAMERICAN_AMERICA.ZHS16GBK;export NLS_LANG" >> ~/.bash_profilesource ~/.bash_profileofile

几种实现子容器水平垂直居中的方法

几种实现子容器水平垂直居中的方法 前言子容器Flexbox 布局position绝对定位transform表格布局 缺点 前言 本文主要讲解有几种方法可以实现容器与子容器水平垂直居中&#xff0c;这些方法的使用方式。那么好&#xff0c;本文正式开始。 子容器 Flexbox 布局 <div style&…

经纬恒润马来西亚工厂正式投入试运行

2023年11月&#xff0c;经纬恒润在中国境外的第一家工厂正式投入试运行。新工厂位于马来西亚&#xff0c;于2023年4月开始筹建&#xff0c;规划总产能500万个汽车电子控制器&#xff0c;主要用于生产新能源汽车电子产品&#xff0c;以满足国外客户日益增长的需求。 经纬恒润马来…

C++编译器对临时对象的优化

思考&#xff1a;我们在构造运算符重载号重载的时候会构造那些函数呐&#xff1f;&#xff1f;&#xff1f; 例子&#xff1a;小dome //该运算重载函数 由 左操作数调用&#xff0c;右操作数当做实参传递给该函数//触发t1t3->t1.operator (t3)Test operator (const Test &a…

印刷设备丝杆选择研磨杆还是冷轧杆好?

在印刷设备中&#xff0c;选择研磨杆还是冷轧杆取决于具体的使用需求和设备要求。以下是关于两种丝杆选择的要点&#xff1a; 1、精度要求&#xff1a;研磨杆通常具有更高的制造精度&#xff0c;能够有效保证印刷设备的精度和稳定性。研磨杆经过精细研磨和校准&#xff0c;具有…

Express基本接口开发-入门学习

前提推荐 任何一个新的知识都是从文档看起&#xff0c;因此express官方文档示例有必要去学习一遍。 推荐看&#xff1a; 推荐入门指南-路由指南-中间件 看完这几个内容之后心里大概知道express有些什么东西了&#xff0c;然后现在就可以去练习了 注意&#xff1a;更多示例-代…

什么猫罐头好吃?猫咪嘎嘎炫的5款猫主食罐头推荐!

想必铲屎官都知道给猫咪长期吃主食罐头的好处了吧&#xff01;主食罐头不仅营养丰富&#xff0c;还能让猫咪顺便补充水分。有时候猫咪食欲不佳&#xff0c;一罐猫主食罐头就能让它们胃口大开呢~ 作为家里有3只猫的铲屎官来说&#xff0c;养猫的这几年可以说血泪史了&#xff0…

GreenCloud VPS 重装系统后无法 SSH 的解决方法

发布于 2023-07-17 在 https://chenhaotian.top/vps/greencloud-ssh-fix/ 解决方法 发工单让客服解决即可。 操作过程 Tu Pham Operator 客服 Hello, We have fixed your problem, please try again! Thanks! Tu Pham, Senior Technician - GreenCloudVPS 17th July 2023…

微信个人号api

简要描述&#xff1a; 登录E云平台 请求URL&#xff1a; http://域名地址/member/login域名地址开发者账号密码:后台系统自助开通 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/json 参数&#xff1a; 参数名必选类型说…

Unity--互动组件(Toggle)

1.组件的可交互 2.组件的过渡状态 3.组件的导航 4.Toggle的属性和参数设置 Toggle 切换控制是一个复选框&#xff0c;允许用户打开或关闭的一个选项&#xff1b; ”Toggle的属性和参数&#xff1a;“” Is on&#xff1a;&#xff08;开启&#xff09; 拨动开关是否从一开…

学生自助查分神秘武器

紧张刺激的期中考试落下帷幕&#xff0c;是不是期待着学生们的成绩揭晓&#xff1f;来&#xff0c;让我这个小红书博主教你&#xff0c;如何利用各种神器&#xff0c;轻松实现学生自助查询成绩&#xff01; 1代码查询&#xff1a; 有些学校已经内置了成绩查询的代码&#xff0…

日常生活小技巧 -- Visual Studio Code 简单使用

讲一下 Visual Studio Code 简单使用&#xff0c;怕以后长时间不用忘记了。 资源管理器 快捷键 ctilshiftE 搜索 快捷键 ctilshiftF 全部折叠 区分大小写、全字匹配 替换、全部替换 切换搜索详细信息 例如排除.h 和 .bat文件 *.h;*.bat源代码管理 暂存更改、放弃更…

BUUCTF 假如给我三天光明 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 下载附件&#xff0c;解压得到一个zip压缩包和一张.jpg图片。 密文&#xff1a; 解题思路&#xff1a; 其实做CTF题时&#xff0c;一定要紧紧的盯着那些明显的事物&#xff0c;优先解决它们&#xff0c;而不是浪…