当前位置: 首页 > news >正文

【C++】如何搞定 C++ 内存管理?

前言:内存管理在C/C++中扮演着重要的角色,同时它也是一把双刃剑,管理得好可以保障程序得稳定、提升运行效率;管理不好就会引发野指针、内存泄露、或者导致程序直接崩溃或异常。所以内存管理对于我们来说还是非常重要的!本篇文章所要讲的new delete等可以帮助你更好的掌握内存管理!

在这里插入图片描述

✨ 坚持用 清晰易懂的图解 + 代码语言, 让每个知识点都 简单直观
🚀 个人主页 :MSTcheng · CSDN
🌱 代码仓库 :MSTcheng · Gitee
📌 专栏系列

  • 📖 《C语言》
  • 🧩 《数据结构》
  • 💡 《C++由浅入深》

💬 座右铭“路虽远行则将至,事虽难做则必成!”

文章目录

  • 一,C/C++的内存分布
    • 1.1C/C++内存分布
    • 1.2函数栈帧的创建和销毁
  • 二,C语言中的动态内存管理方式
  • 三,C++的内存管理方式
    • 3.1 new和delete的简单使用
  • 四,new和delete的实现原理
    • 4.1 operator new和operator delete
    • 4.2 new[]和delete[]的原理
  • 五,定位new
  • 六,malloc/free和new/delete的区别

一,C/C++的内存分布

1.1C/C++内存分布

在C语言阶段学习的时候,总会有一些问题就是我们写过的各种各样的代码,局部变量,全局变量,静态变量等它们到底是存在哪的呢?相信有很多人在学C/C++的时候会有这些疑问,下面就来看看C/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);
}

上面代码中的一些局部变量、全局变量、静态变量、数组、指针等都存在什么地方?

在这里插入图片描述
说明:

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

1.2函数栈帧的创建和销毁

上面我们看到的大部分在函数内部创建的变量,指针,数组,它们基本都是在栈区创建的说明栈区对我们来说也是一块比较重要的区域,那么为什么它们是存在栈区的呢?

  1. 内存管理的高效性: 栈区采用后进先出(LIFO)的分配机制,内存分配和释放仅需移动栈指针,速度极快。函数调用时,栈指针下移分配空间;函数返回时,栈指针上移自动回收内存,无需复杂的内存管理操作。
  2. 生命周期与函数调用匹配: 局部变量的生命周期严格绑定函数执行周期。栈区的自动分配和释放特性完美契合这一需求,避免了手动管理内存的复杂性,减少内存泄漏风险。
    下面我们就简单来看看函数栈帧的创建和销毁:

在这里插入图片描述

下面以一个简单的main函数调用Add函数为例来看看,函数栈帧创建和销毁的具体过程:
在这里插入图片描述

二,C语言中的动态内存管理方式

在C语言中,内存管理是通过我们之前所学过的三个内存函数,malloc calloc realloc来管理的,比如下面这段代码:

void Test ()
{
// 1.malloc/calloc/realloc的区别是什么?int* p2 = (int*)calloc(4, sizeof (int));int* p3 = (int*)realloc(p2, sizeof(int)*10);
// 这里需要free(p2)吗?free(p3 );
}

这两个问题我们在C语言阶段就已经详细的解答过了,在这就不再赘述了。不了解的读者可以去看我之前写的文章:C语言内存函数

三,C++的内存管理方式

3.1 new和delete的简单使用

我们在之前说过,C++是兼容C的用法的,因此C语言哪些内存管理函数像malloc,calloc,realloc函数是可以在C++中去使用的。但是有些地方就无能为力,因此C++就提出了自己的内存管理方式new和delete操作符进行内存管理。

/newdelete的用法
void Test()
{//动态开辟一个int(整形)大小的空间int* ptr4 = new int;//给创建好的空间初始化 使用()圆括号int* ptr5 = new int(1);//动态开辟10个整形大小的空间 相当于一个数组int* ptr6 = new int[10];//给新开辟的空间进行初始化int* ptr7 = new int[10] {1, 2, 3, 4};//隐式类型转换//释放空间用delete delete是一个关键字 不是函数 不需要带括号 delete ptr4;delete[] ptr6;//释放全部空间delete[3] ptr7; //释放ptr7的前3个空间
}

注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[]。注意匹配起来使用。

这时候可能就会有人有疑问:上面的代码不用new和delete使用C语言的内存函数也照样能够完成啊,C++的new和delete的优势在哪呢?

  • 对于内置类型new/delete相比于C语言的malloc/free差别不大,但是如果要操作的不是上面的内置类型而是一个类呢?C语言的内存函数还能胜任吗?答案是不行
class A
{ 
public:A(int a = 0): _a(a){cout << "A():" << this << endl;cout << _a << endl;} ~A(){cout << "~A():" << this << endl;}
private:int _a;
}
int main()
{
//使用C语言的内存函数开辟一块空间A* p1 = (A*)malloc(sizeof(A));//初始化//p1->_a=10;在初始化的时候由于_a是私有成员 我们无法在外面访问 所以也就无法初始化free(p1);//而new创建的对象 就会去调用对应的构造//A* p2 = new A(); 不传参数就调用默认构造为100A* p2 = new A(10);//delete就会去调用对应的析构函数delete p2;
}

在这里插入图片描述

所以,从上面的代码我们就能看出new和deletemalloc free最大的区别就是:new除了开空间还会去调用类中的构造函数;delete就会去调用类中的析构函数。而mallocfree就不会。

总结:对于内置类型,malloc/free和new/delete没有什么太大的区别,但是对于自定义类型来讲new/delete会调用构造和析构,malloc/free不会。所以从本质上讲malloc/free不能自动的去初始化和释放资源,但是new和delete可以!

四,new和delete的实现原理

4.1 operator new和operator delete

typedef int STDataType;
class Stack
{
public:Stack(int n = 4){cout << "Stack(int n = 4)" << endl;_a = (STDataType*)malloc(sizeof(STDataType) * n);if (nullptr == _a){perror("malloc申请空间失败");return;}_capacity = n;_top = 0;}~Stack(){cout << "~Stack()" << endl;free(_a);_a = nullptr;_top = _capacity = 0;}private:STDataType* _a;size_t    _capacity;size_t    _top;
};
int main()
{Stack* p1 = new Stack(10);delete p1;//对于内置类型没有资源释放可以new 和free配对用 但还是建议与delete配对用int* p2 = new int(1);//free(p2);delete p2;return 0;
}

在这里插入图片描述

总结上面的内容就是:当调用new时,就会去调用operator new+构造;当调用delete时就会调用析构+delete 注意这里要区分delete和析构,析构不一定释放资源,有资源才释放,而delete是一定释放资源,不能将二者混淆!
当然如果去看operator new和operator delete这两个函数的源码的话就会发现这两个函数的源码其实也是用mallocfree来实现的只不过是对它们进行了封装而已。

那就会有人问了:既然newdelete本质上也使用mallocfree那么为什么还要搞operator new和operator delete呢?

  • 其实是因为C++更喜欢使用抛异常这种方式来应对空间不足的情况,像malloc我们之前就会写一个if判断语句去判断它开辟成功还是开辟失败,但是new如果开辟失败那就会抛异常。(异常在后面章节会介绍,目前就作为了解知识)

4.2 new[]和delete[]的原理

了解完了上面new和delete的原理,下面我们来看看new[]和delete[]的原理:

int main()
{A* p1 = new A[10];delete[] p1;A* p1 = (A*)malloc(sizeof(A));//malloc开辟的空间 要手动调用析构p1->~A();free(p1);return 0;
}

在这里插入图片描述

五,定位new

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

class A
{ 
public:A(int a = 0): _a(a){cout << "A():" << this << endl;} ~A(){cout << "~A():" << this << endl;}
private:int _a;
};
int main()
{
// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行A* p1 = (A*)malloc(sizeof(A));new(p1)A; //此处要想调用构造函数就必须new(对象) 如果构造函数有参数就需要传参p1->~A();free(p1);A* p2 = (A*)operator new(sizeof(A));new(p2)A(10);p2->~A();operator delete(p2);
return 0;
}

定位 new 的特点:

  1. 不分配内存:仅调用构造函数,内存由用户预先分配。
  2. 显式析构调用:对象析构需手动调用,因为定位 new 不管理内存生命周期。
  3. 无异常抛出:普通 new 在分配失败时抛出 std::bad_alloc,而定位 new 假设内存已有效。
    定位new表达式在实际中一般是配合内存池使用,感兴趣的读者可以去了解一下内存池。

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

以下是 malloc/freenew/delete 的比较表格:

比较维度malloc/freenew/delete
本质函数操作符
初始化不初始化申请的空间可通过构造函数初始化
空间大小计算需手动计算并传递(例如 sizeof(int) * 10自动计算,只需指定类型和数量如果是多个对象[]中指定对象个数即可(例如 new int[10]
返回值类型返回 void*,使用时必需强制转换返回与类型匹配的指针,无需转换,但要捕获异常
失败处理返回 NULL,需手动判空抛出异常(如 std::bad_alloc),需捕获处理
自定义类型对象处理(底层功能)仅分配/释放内存,不调用构造或析构函数分配内存后调用构造函数完成初始化,释放前调用析构函数完成空间中资源的清理释放
以上就是本篇文章的所有内容了,感谢各位大佬观看,制作不易还望各位大佬点赞支持一下!有什么问题可以加我私信交流!
http://www.dtcms.com/a/406870.html

相关文章:

  • 关于做情侣的网站的图片素材网络营销效果评估的作用有哪些
  • 网站制作洋网络泰安最新消息
  • 中科君达视界千眼狼科学仪器赋能“芯屏汽合”制造
  • 案例分享:增材制造的负泊松比材料拉胀测试-VIC-3D高空间分辨率DIC系统在增材制造复杂结构中的应用
  • 安卓接入Max广告源
  • Rockchip平台 Android 11 到 Android 16 系统占用内存对比分析
  • 简洁文章类织梦网站模板郑州企业服务公司
  • 企业网站的需求是什么WordPress網站放ICP
  • Avalonia:创建安卓、Web应用(部署到iis)
  • three.js —— 引入模型
  • 一次跨端数据类型兼容问题的排查与解决(判断类型)
  • Fscan:内网综合扫描工具深度入门指南
  • 什么时候能用ipv6做网站二手房网
  • 免费网站管理系统下载mvc5网站开发实战详解
  • 零基础从头教学Linux(Day 42)
  • 前端拿到标准省市区数据
  • (六)重构的艺术:简化复杂条件逻辑的秘诀
  • 雏光 网络推广 网站建设ps模板素材网站
  • 高可用MySQL的整体解决方案、体系化原理和指导思路
  • yoda_formatting_func函数解析(105)
  • Vue 3 中 routes 与 route 的详解
  • 哪有做网站推广wordpress 在线编辑器
  • leetcode_138 随机链表的复制
  • Kendo UI for jQuery 2025 Q3新版亮点 - AI 智能网格与全新表单体验
  • 职业规划之软件测试工作五年后,做技术还是做管理?
  • 【一文了解】C#的StringSplitOptions枚举
  • 大连仟亿科技网站建设公司 概况网站搜索 代码
  • 高端网站设计中的微交互:细节如何决定用户体验
  • 香港科技大学提出融合神经网络框架,高效预测蛋白质序列的多金属结合位点
  • 9.9奶茶项目:matlab+FPGA的cordic算法计算±π之间的sin和cos值