C++内存管理

目录

一.new和delete

二.operator new与operator delete函数

三.new和delete的实现原理

四.在VS2022编译器下new和delete不匹配的问题

五.定位new

六.malloc/free和new/delete的区别

七.C++内存分布 

八.内存泄漏


C++在内存管理上引入了两个操作符,分别是new,delete,和malloc/free有许多不同,比如后者是函数前者是操作符,但new/delete又是基于malloc/free实现的。

一.new和delete

        C++的内存管理和C有不同,C使用malloc/calloc/realloc申请空间,free释放空间。C++是使用new和delete来申请和释放空间。下面是new和delete的用法:

#include<iostream>
using namespace std;int main()
{// 动态申请一个int类型的空间int* ptr1 = new int;// 动态申请一个int类型的空间并初始化为10int* ptr2 = new int(10);// 动态申请10个int类型的空间int* ptr3 = new int[3];//动态申请10个int类型空间并初始化int* ptr4 = new int[3] {};//里面按顺序填写初始化的值,没有就是0delete ptr1;delete ptr2;delete[] ptr3;delete[] ptr4;return 0;
}

注意:在申请自定义类型空间时new和delete会自动调用构造和析构函数,new申请空间失败会抛异常! 这些都和C的内存函数不同。而且new[]和delete[]要匹配使用!

二.operator new与operator delete函数

       operator new和operator delete是两个系统提供的全局函数。

        在使用new时new在底层会调用operator new函数,本质上使用的还是malloc函数来开辟空间,还会判断malloc是否开辟成功(不像C需要用户自己判断),成功就返回,失败就会执行用户提供的空间不足应对措施,如果用户没有提供就抛异常(C++11新特性)。

        使用delete时delete会在底层调用operator delete函数,本质上是使用free来释放空间

        注意:operator new/delete对于自定义类型是不会自动调用构造析构的,只有使用new/delete才会!

//库里面operator new的实现代码
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytesvoid *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 delete的实现代码
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和delete的实现原理

        对于内置类型new,delete和malloc/calloc/realloc,free没什么区别,申请空间失败new会抛异常,malloc会返回空指针。new/delete是申请/释放单个元素空间,new[]/delete[]申请/释放的是连续空间。

        对于自定义类型malloc/calloc/realloc,free和内置类型完全一样,new/delete分情况讨论:

1.new原理

        (1). 调用operator new函数申请空间

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

四.在VS2022编译器下new和delete不匹配的问题

        下面是两段代码,都是new和delete不匹配的问题,但是一段没报错一段报错。

#include<iostream>
using namespace std;class A
{
public:A(){cout << "A()" << endl;}
private:int a = 1;
};int main()
{A* ptr = new A[10];delete ptr;//这里delete和new[]不匹配但没报错return 0;
}

下面会报错: 

#include<iostream>
using namespace std;class A
{
public:A(){cout << "A()" << endl;}~A()//显示写析构函数{}
private:int a = 1;
};int main()
{A* ptr = new A[10];delete ptr;//这里delete和new[]不匹配但是显示写析构函数却报错了return 0;
}

上面的代码只差了一个析构函数就报错了。

        原因是new[]的时候不仅开辟用户需要的空间,还会再开辟的空间前四字节额外开辟一小段空间,里面存放new[]里面的数字。作用是delete[]时告诉delete[]要调用析构函数几次。然后再释放空间,这里还会把前面额外开辟的4字节空间也释放掉。

        但如果是直接用delete释放空间,那么就不会释放前面额外开辟的4字节空间就会报错(代码中ptr指针指向的地址是4字节之后的位置,所以就会导致直接释放空间会漏掉这4字节)。

        上面的情况是有显示写析构函数,如果没有写析构函数编译器会优化,认为默认生成的析构函数没啥作用就在new[]的时候不开辟额外的4字节空间,所以直接用delete释放ptr指向的空间就不会报错。

五.定位new

        定位new是在已分配的内存空间中调用构造函数初始化一个对象。

使用格式:
new (place_address) type 或者 new (place_address) type(initializer-list)
place_address 必须是一个指针, initializer-list 是类型的初始化列表

使用场景:

定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始 化。
#include<iostream>
using namespace std;class A
{
public:A(int a = 0): _a(a){cout << "A():" << this << endl;}~A(){cout << "~A():" << this << endl;}
private:int _a;
};// 定位new/replacement new
int main()
{// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行A* p1 = (A*)malloc(sizeof(A));new(p1)A;// 注意:如果A类的构造函数有参数时,此处需要传参p1->~A();free(p1);A* p2 = (A*)operator new(sizeof(A));//这里也只是开空间,执行构造函数new(p2)A(10);p2->~A();operator delete(p2);return 0;
}

六.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 在释放空间前会调用析构函数完成空间中资源的清理

七.C++内存分布 

        先来看一段代码:

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);
}

        回答下列问题:

选项 :             A .                B .                C . 数据段 ( 静态区 )               D . 代码段 ( 常量区 )
globalVar在哪里? ____   staticGlobalVar 在哪里?____
staticVar在哪里? ____   localVar 在哪里?____
num1 在哪里?____
char2在哪里? ____   * char2 在哪里?___
pChar3在哪里? ____       * pChar3 在哪里?____
ptr1在哪里? ____         * ptr1 在哪里?____
答案从左到右从上到下分别是:C  C  C  A  A  A  A  A  D  A  B

填空题:

​​​​​​​ sizeof ( num1 ) = ____ ;
          sizeof ( char2 ) = ____ ;         strlen ( char2 ) = ____ ;
sizeof ( pChar3 ) = ____ ;     strlen ( pChar3 ) = ____ ;
sizeof ( ptr1 ) = ____ ;
答案从左到右从上到下分别是:40  5  4  4/8(取决于32位还是64位机器)  4  4/8

上图是C++内存分布,下面是说明:

1. 又叫堆栈, 非静态局部变量 / 函数参数 / 返回值等等,栈是向下增长的。
2. 内存映射段 是高效的 I/O 映射方式,用于装载一个共享的动态内存库。用户可使用系统接口
创建共享共享内存,做进程间通信。
3. 用于程序运行时动态内存分配,堆是可以上增长的。
4. 数据段 -- 存储全局数据和静态数据。
5. 代码段 -- 可执行的代码 / 只读常量

八.内存泄漏

        什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费,比如new了之后的空间没有delete。
        内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。
        服务器需要长期运行,如果有内存泄漏就算是一点点,长期运行下来也会积少成多,然后导致服务器卡死或者重启,损失都是很大的。
        内存泄漏分类:
        堆内存泄漏(Heap leak)
堆内存指的是程序执行中依据须要分配通过 malloc / calloc / realloc / new 等从堆中分配的一
块内存,用完后必须通过调用相应的 free 或者 delete 删掉。假设程序的设计错误导致这部分
内存没有被释放,那么以后这部分空间将无法再被使用,就会产生 Heap Leak
        系统资源泄漏
指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放
掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

        如何检测内存泄漏:

        VS可以在main函数之后加上windows系统提供的_CrtDumpMemoryLeaks()函数进行检测,但是只能返回大概泄漏的多少字节,没有准确的位置信息。

        避免内存泄露就应该在平时养成良好的编程习惯,申请的空间记得释放回去。

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

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

相关文章

Tailwind CSS 速成

Tailwind CSS 速成 完成了 responsive 和特效的学习后&#xff0c;现在折腾一下 tailwind CSS&#xff0c;这个 CSS 库本身就包含了很多的 utility class&#xff0c;之前跟着 yt 的视频写项目的时候&#xff0c;写了两个项目&#xff0c;好像不记得写过 CSS…… Redux Toolk…

SpringMVC的拦截器和JSR303的使用

目录 一、JSR303 二、拦截器&#xff08;interceptor&#xff09; 一、JSR303 1.1.什么是JSR303 JSR 303&#xff0c;它是Java EE&#xff08;现在称为Jakarta EE&#xff09;规范中的一部分。JSR 303定义了一种用于验证Java对象的标准规范&#xff0c;也称为Bean验证。 Bean验…

Golang goroutine 进程、线程、并发、并行

goroutine 看一个需求 需求&#xff1a;要求统计1-200000000000的数字中&#xff0c;哪些是素数? 分析思路&#xff1a; 1)传统的方法&#xff0c;就是使用一个循环&#xff0c;循环的判断各个数是不是素数&#xff08;一个任务就分配给一个cpu去做&#xff0c;这样很不划算…

运动耳机哪个好、最好的运动牌子排名榜

很多朋友喜欢在运动的时候听音乐&#xff0c;为此&#xff0c;他们会为自己配备一款蓝牙耳机或是运动耳机&#xff0c;可以在运动的时候随身听&#xff0c;可是&#xff0c;一些人在挑选耳机的时候犯难了&#xff0c;市面上那么多运动耳机&#xff0c;运动耳机哪个好&#xff1…

关于rsync用不了之后

1.尝试找出rsync使用错误原因&#xff1a; 我遇见一个问题&#xff1a;rsync:read errors mapping&#xff1a;communication error on send &#xff08;70&#xff09;&#xff0c;我查了一下这个问题很大可能是网络链接导致的&#xff0c;然后我用nslookup指令查看了/train2…

测试平台项目部署二(手动部署改成Dockerfile)

测试平台项目部署二(手动部署改成Dockerfile) 一、Dockerfile制作1、entrypoint.sh制作2、构建镜像3、启动容器二、遇到的问题1、pip install --no-cache-dir -r requirements.txt安装第三方库时,报Installing build dependencies: started2、安装第三方库文件比较慢,考虑更…

Vue3自定义指令

文章目录 Vue3自定义指令1. 自定义全局指令v-focus2. 自定义局部指令v-focus3. 指令定义的钩子函数3.1 概念3.2 钩子函数参数3.3 vnode & prevNode3.4 简写3.5 指令函数接受JavaScript表达式 Vue3自定义指令 1. 自定义全局指令v-focus 除了默认设置的核心指令( v-model 和…

zabbix -- 安装

Zabbix zabbix除了可以监控linux服务器之外&#xff0c;还可以监控路由器、交换机、容器等&#xff0c;全方位监控 Zabbix对服务器的监控是通过在服务器上部署“间谍”程序zabbix-agent获取数据&#xff0c;但对于路由器、交换机等机器的监控不能进行部署&#xff0c;这个时候…

Navicat导入Excel数据顺序变了

项目场景&#xff1a; Navicat导入Excel数据 问题描述 从Excel表格中导入数据到数据库中。但是&#xff0c;在导入的过程中&#xff0c;我们常会发现数据顺序出现了问题&#xff0c;导致数据错位&#xff0c;给数据的处理带来了极大的麻烦。 原因分析&#xff1a; 这个问题的…

2023备战秋招Java面试八股文合集

Java就业大环境仍然根基稳定&#xff0c;市场上有很多机会&#xff0c;技术好的人前景就好&#xff0c;就看你有多大本事了。小编得到了一份很不错的资源&#xff0c;建议大家可以认真地来看看以下的资料&#xff0c;来提升一下自己的核心竞争力&#xff0c;在面试中轻松应对面…

Vue3+Element Plus实现el-table跨行显示(非脚手架)

Vue3Element Plus实现el-table跨行显示 app组件内容使用:span-method"objectSpanMethod"自定义方法实现跨行显示查询方法初始化挂载新建一个html即可进行测试&#xff0c;完整代码如下效果图 app组件内容 <div id"app"><!-- 远程搜索 --><e…

webpack自定义loader解析指定后缀名文件

案例&#xff1a; webpack自定义loader解析.chenjiang后缀名的文件 整体目录&#xff1a; chenjiangLoader.js文件代码 // 正则匹配script标签中的内容 const REG /<script>([\s\S]*)<\/script>/;module.exports function (source) {const __source source.…