c++学习之内存管理

目录

1.c/c++内存分布

2.new与delete/malloc与free

c++内存管理方式:

new/delete操作内置类型:

new/delete操作自定义类型

 operator new与operator delete函数

new和delete的实现原理

 内置类型

自定义类型

malloc/free和new/delete的区别


1.c/c++内存分布

在学习c语言的时候我们就已经了解到不同的数据存储在不同的空间当中,对于内存,其被划分成不同的各个区域,分别用来管理不同的数据,如下图:

可以看到内存被划分为堆区,栈区,静态区,代码段区,内存映射段,数据段。
每一种区域都存放着不同的数据。
1. 又叫堆栈 -- 非静态局部变量 / 函数参数 / 返回值等等,栈是向下增长的。
我们常定义的函数,普通变量等都存放在栈区。
2. 内存映射段 是高效的 I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口
创建共享共享内存,做进程间通信。( Linux 课程如果没学到这块,现在只需要了解一下)
3. 用于程序运行时动态内存分配,堆是可以向上增长的。
对于数据结构中,我们常常开辟堆区的内存保存数据,在c++中也是如此且有新方法。
4. 数据段 -- 存储全局数据和静态数据。static修饰的全局变量与定义的全局变量存放处。
5. 代码段 -- 可执行的代码 / 只读常量。

其次, c++在继承c语言的基础上,对于堆区上的申请和释放做了优化,这将体现在c++类和自定义数据类型中。

2.new与delete/malloc与free

首先我们知道如何利用c语言的方式向堆区上开辟空间,我们利用malloc,cealloc,realloc这三个函数可以实现向堆区上开辟一块空间,之后我们在进行初始化,在对空间的利用完之后,利用free函数释放掉对上的空间,实现动态内存管理。

如下代码:
int main()
{char* p1 = (char*)malloc(sizeof(char)*10); free(p1);char* p2 = (char*)calloc(char ("hello world"), sizeof(char)*10);char *p3 = (char*)realloc(p2, sizeof(char) * 10);free(p3);return 0;
}

对于c语言的动态内存管理:

1.首先大佬认为如此的设计使得动态内存管理过于冗余,对于空间的开辟较为麻烦。

2.我们需要强制类型转换并计算出所需该类型的大小。

c++内存管理方式:

C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因
此C++又提出了自己的内存管理方式 通过new和delete操作符进行动态内存管理

new/delete操作内置类型:

 我们能看到对于new的一些操作及运算符重载。

对于c++:我们可以利用new来进行动态内存的申请,利用delete进行内存的释放。

void Test()
{// 动态申请一个int类型的空间int* ptr4 = new int;// 动态申请一个int类型的空间并初始化为10int* ptr5 = new int(10);// 动态申请10个int类型的空间int* ptr6 = new int[3];//动态申请5个int类型的空间并初始化int *ptr7=new int[5]{1,2,3,4,5};delete ptr4;delete ptr5;delete[] ptr6;delete[] ptr7;
}

可以看到我们可以初始化空间,并且对于多个内存的开辟也更加方便,对于大小可以自己推导,如一个整型四个字节,一个字符型一个字节。

注意:这里在开辟多个或一个空间时,delete的写法也应与之对应,否则存在释放报错。

因为c++兼容c语言,所以除了书写简便,对于内置类型,功能是一样的。

new/delete操作自定义类型

如果仅仅是因为优化写法,那么在c++设计new和delete是纯属没有必要的,真正牛逼得地方是new与delete可以操作自定义类型!!!而这与c++11类和对象密切联系着。

利用malloc与free我们只能做到对空间的开辟与释放,但无法去初始化这个自定义类型的空间。

如自定义类型A:

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

对于自定义类型下的malloc:

void test1()
{A* p1 = (A*)malloc(sizeof(A));//无法调用构造函数来初始化,因为当前无法显式调用构造函数p1->_a = 5;//也无法对私有数据操作,只能通过定义接口函数来初始化p1->init_a(5);//这也是对于简单的数据,若是设计的类中数据量偌大,则直接就g了free(p1);
}

 我们可以看到自定义类型的数据要实现初始化太困难且复杂,甚至没法用。

那么对于重新设计的new与delete:

int main()
{// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间//还会调用构造函数和析构函数A* p1 = new A;A* p2 = new A(5);A* p3 = new A[2]{1,2};delete p1;delete p2;delete[]p3;return 0;
}

对于初始化new可以再申请空间后调用构造函数,对于释放delete会调用析构函数,之后在释放空间。

 

 operator newoperator delete函数

new delete 是用户进行 动态内存申请和释放的操作符 operator new operator delete
系统提供的 全局函数 new 在底层调用 operator new 全局函数来申请空间, delete 在底层通过
operator delete 全局函数来释放空间。
注意这两个作为函数(对于new和delete的实现),并非是new与delete的重载。
如下段代码:
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
比特就业课
通过上述两个全局函数的实现知道,operator new 实际也是通过malloc来申请空间,如果
malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施
就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。
5. new和delete的实现原理
5.1 内置类型if (_callnewh(size) == 0){// report no memory// 如果申请内存失败了,这里会抛出bad_alloc 类型异常static const std::bad_alloc nomem;_RAISE(nomem);}
return (p);
}void operator delete(void *pUserData)
{_CrtMemBlockHeader * pHead;RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));if (pUserData == NULL)return;_mlock(_HEAP_LOCK);  /* block other threads */__TRY/* get a pointer to memory block header */pHead = pHdr(pUserData);/* verify block type */_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));_free_dbg( pUserData, pHead->nBlockUse );__FINALLY_munlock(_HEAP_LOCK);  /* release other threads */__END_TRY_FINALLYreturn;
}
/*

对于这里的new内存开辟失败的时候,这里设计利用异常进行判断。

通过上述两个全局函数的实现知道,operator new 实际也是通过malloc来申请空间,如果
malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施
就继续申请,否则就抛异常,程序遇到异常就直接终止了。operator delete 最终是通过free来释放空间的

newdelete的实现原理

 内置类型

 if (_callnewh(size) == 0){// report no memory// 如果申请内存失败了,这里会抛出bad_alloc 类型异常static const std::bad_alloc nomem;_RAISE(nomem);}
return (p);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void *pUserData)
{_CrtMemBlockHeader * pHead;RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));if (pUserData == NULL)return;_mlock(_HEAP_LOCK);  /* block other threads */__TRY/* get a pointer to memory block header */pHead = pHdr(pUserData);/* verify block type */_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));_free_dbg( pUserData, pHead->nBlockUse );__FINALLY_munlock(_HEAP_LOCK);  /* release other threads */__END_TRY_FINALLYreturn;
}
/*
free的实现
*/
#define   free(p)               _free_dbg(p, _NORMAL_BLOCK)

如果申请的是内置类型的空间, new malloc delete free 基本类似,不同的地方是:
new/delete 申请和释放的是单个元素的空间, new[] delete[] 申请的是连续空间,而且 new 在申
请空间失败时会抛异常, malloc 会返回 NULL

自定义类型

new 的原理
1. 调用 operator new 函数申请空间
2. 在申请的空间上执行构造函数,完成对象的构造
delete 的原理
1. 在空间上执行析构函数,完成对象中资源的清理工作
2. 调用 operator delete 函数释放对象的空间
new T[N] 的原理
1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对
象空间的申请
2. 在申请的空间上执行 N 次构造函数
delete[] 的原理
1. 在释放的对象空间上执行 N 次析构函数,完成 N 个对象中资源的清理
2. 调用 operator delete[] 释放空间,实际在 operator delete[] 中调用 operator delete 来释
放空间

malloc/freenew/delete的区别

malloc/free new/delete 的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地
方是:
1. malloc free 是函数, new delete 是操作符
2. malloc 申请的空间不会初始化, new 可以初始化
3. malloc 申请空间时,需要手动计算空间大小并传递, new 只需在其后跟上空间的类型即可,
如果是多个对象, [] 中指定对象个数即可
4. malloc 的返回值为 void*, 在使用时必须强转, new 不需要,因为 new 后跟的是空间的类型
5. malloc 申请空间失败时,返回的是 NULL ,因此使用时必须判空, new 不需要,但是 new
要捕获异常
6. 申请自定义类型对象时, malloc/free 只会开辟空间,不会调用构造函数与析构函数,而 new
在申请空间后会调用构造函数完成对象的初始化, delete 在释放空间前会调用析构函数完成
空间中资源的清理

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

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

相关文章

解决Pandas KeyError: “None of [Index([...])] are in the [columns]“问题

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

【MyBatis】:PageHelper分页插件与特殊字符处理

目录 一、PageHelper介绍 二、PageHelper使用 1. 导入pom依赖 2. Mybatis.cfg.xml 配置拦截器 3. 配置 Mapper.xml 4. 编写测试 三、特殊字符处理 1. 使用转义字符 2. 使用CDATA 区段 一、PageHelper介绍 PageHelper 是 Mybatis 的一个插件&#xff0c;这里就不扯了&a…

Blazor 依赖注入妙用:巧设回调

文章目录 前言依赖注入特性需求解决方案示意图 前言 依赖注入我之前写过一篇文章&#xff0c;没看过的可以看看这个。 C# Blazor 学习笔记(10):依赖注入 依赖注入特性 只能Razor组件中注入所有Razor组件在作用域注入的都是同一个依赖。作用域可以看看我之前的文章。 需求 …

【Vue框架】 router和route是什么关系

前言 之前没太注意&#xff0c;写着写着突然发现它们貌似不太一样&#xff0c;记录以下&#xff0c;回顾的看总结就好。 1、总结✨ route&#xff1a;当前激活路由的对象&#xff0c;用于访问和操作当前路由的信息 router&#xff1a;管理多个route的对象&#xff0c;整个应…

opencv-疲劳检测-眨眼检测

#导入工具包 from scipy.spatial import distance as dist from collections import OrderedDict import numpy as np import argparse import time import dlib import cv2FACIAL_LANDMARKS_68_IDXS OrderedDict([("mouth", (48, 68)),("right_eyebrow",…

SAP ABAPG开发屏幕自动生成日期的搜索帮助

代码如下&#xff1a; REPORT z_jason_test_f4 . TABLES: s031. PARAMETER p_spmon TYPE spmon DEFAULT sy-datum0(6) OBLIGATORY. SELECT-OPTIONS s_spmon FOR s031-spmon DEFAULT sy-datum0(6) OBLIGATORY. AT SELECTION-SCREEN ON VALUE-REQUEST…

C#系统锁屏事件例子 - 开源研究系列文章

今天有个网友问了个关于操作系统锁屏的问题。 我们知道&#xff0c;操作系统是基于消息和事件处理的&#xff0c;所以我们只要找到该操作系统锁屏和解屏的那个事件&#xff0c;然后在事件里进行处理即可。下面是例子介绍。 1、 项目目录&#xff1b; 下面是项目目录&#xff1a…

STL vector基本用法

目录 vector介绍vector的使用vector的定义vector接口函数构造函数和赋值重载迭代器元素访问vector容量相关函数vector增加 删除 修改操作关系操作符 vector介绍 vector是可变大小数组的序列容器。像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。也就是意味着可…

Datawhale Django后端开发入门 TASK03 QuerySet和Instance、APIVIew

一、QuerySet QuerySet 是 Django 中的一个查询集合&#xff0c;它是由 Model.objects 方法返回的&#xff0c;并且可以用于生成数据库中所有满足一定条件的对象的列表。 QuerySet 在 Django 中表示从数据库中获取的对象集合,它是一个可迭代的、类似列表的对象集合。主要特点…

睿思BI旗舰版V5.3正式发布

发布时间&#xff1a;2023-7-20 主要更新内容: 1.增加3D地图功能 2.增加水球图 3.增加扇形图&#xff0c;在数据大屏 - 自定义组件中定义。 4.增加指标引导线功能&#xff0c;在数据大屏 - 自定义组件中定义。 5.详情页增加回调函数功能。 6.大屏/仪表盘模版下载&#xff0c;…

java八股文面试[JVM]——JVM参数

参考&#xff1a;JVM学习笔记&#xff08;一&#xff09;_卷心菜不卷Iris的博客-CSDN博客 堆参数调优入门 jdk1.7&#xff1a; jdk1.8&#xff1a; 面试题&#xff1a;给定-Xms Xmx -Xmn 问 最大的eden区域是多少M。 常用JVM参数 怎么对jvm进行调优&#xff1f;通过参数配…