C++从入门到实战(十二)详细讲解C++如何实现内存管理

article/2025/8/15 6:01:58

C++从入门到实战(十二)详细讲解C++如何实现内存管理

  • 前言
  • 一、C++内存管理方式
    • 1. new/delete操作内置类型
    • 2. 异常与内存管理的联系(简单了解)
    • 3. new和delete操作自定义类型
  • 二、 operator new与operator delete函数(重点)
    • 1. new和delete操作符与operator new和operator delete函数的关系
    • 2. operator new函数的工作原理
    • 3. operator delete函数的工作原理
  • 三、 定位new表达式(placement-new) (了解即可)
    • 1. 定位 new 表达式的概念
    • 2. 定位 new 表达式的使用格式
    • 3. 定位 new 表达式的使用场景
  • 四、malloc/free和new/delete的区别
    • 1. 相同点
    • 2. 不同点
      • 一个是函数,一个是操作符
      • new 会 “初始化”,malloc 不会
      • 空间大小:new 自动计算,malloc 要手动算
      • new 不用强转,malloc 需要
      • 错误处理:malloc 返 NULL,new 抛异常
      • 自定义类型:new/delete 会 “照顾” 对象,malloc/free 不会


前言

  • 在上一篇博客中,我们探讨了 C/C++ 语言的内存分布模型,并对比了 C 与 C++ 内存管理的核心差异,初步认识了 C++ 中new与delete的基本概念,为理解 C++ 内存管理体系奠定了基础
  • 本文将在此基础上,深入解析 C++ 内存管理的核心机制,从底层原理到实践细节展开系统讲解,帮助读者全面掌握这一重要知识模块

我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的C++知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12880513.html?spm=1001.2014.3001.5482


一、C++内存管理方式

1. new/delete操作内置类型

  • 在 C 语言里有它自己的内存管理办法,但用起来比较麻烦,而且有些情况处理不了。
  • C++ 为了解决这些问题,引入了new和delete操作符来进行动态内存管理
#include <iostream>using namespace std;
void Test()
{int* ptr4 = new int;// 动态申请一个int类型的空间int* ptr5 = new int(10);//动态申请一个int类型的空间并初始化为10int* ptr6 = new int[3];//动态申请3个int类型的空间delete ptr4;delete ptr5;//delete ptr6;//错误在 C++ 中,new 和 delete、new[] 和 delete[] 是成对出现的操作符,它们的使用需要遵循特定的配对规则://如果使用 delete 而非 delete[] 来释放通过 new[] 分配的数组内存,会产生严重的后果://内存泄漏:delete 只会释放数组首元素的内存,而不会释放数组中其他元素的内存,这就导致了部分内存无法被回收,造成内存泄漏。//未定义行为:使用 delete 释放数组内存属于未定义行为,这意味着程序可能会出现各种不可预测的问题,比如程序崩溃、数据损坏等delete[] ptr6;
}
  • 在 C++ 中,new 和 delete、new[] 和 delete[] 是成对出现的操作符,它们的使用需要遵循特定的配对规则
  • 如果使用 delete 而非 delete[] 来释放通过 new[] 分配的数组内存,会产生严重的后果:例如上面代码中的int* ptr6 = new int[3]与delete ptr6

内存泄漏:delete 只会释放数组首元素的内存,而不会释放数组中其他元素的内存,这就导致了部分内存无法被回收,造成内存泄漏。
未定义行为:使用 delete 释放数组内存属于未定义行为,这意味着程序可能会出现各种不可预测的问题,比如程序崩溃、数据损坏等

在这里插入图片描述

2. 异常与内存管理的联系(简单了解)

  • 在使用new申请内存时,如果系统没有足够的内存可供分配,就可能会抛出异常。
  • 所以在进行内存管理时,要考虑到这种可能出现的异常情况,确保程序的健壮性。
#include <iostream>
using namespace std;void Test() {try {// 动态申请一个 int 类型的空间int* ptr4 = new int;// 动态申请一个 int 类型的空间并初始化为 10int* ptr5 = new int(10);// 动态申请 3 个 int 类型的空间int* ptr6 = new int[3];// 使用分配的内存*ptr4 = 5;cout << "Value of ptr4: " << *ptr4 << endl;cout << "Value of ptr5: " << *ptr5 << endl;for (int i = 0; i < 3; ++i) {ptr6[i] = i;cout << "Value of ptr6[" << i << "]: " << ptr6[i] << endl;}// 释放内存delete ptr4;delete ptr5;delete[] ptr6;} catch (const bad_alloc& e) {// 捕获内存分配失败的异常cerr << "Memory allocation failed: " << e.what() << endl;}
}int main() {Test();return 0;
}    

在这里插入图片描述

#include <iostream>
using namespace std;void Func()
{int i = 1;while (1){int* p1 = new int[1024 * 1024];cout << i << "->" << p1 << endl;i++;}
}int main()
{try{Func();}catch (const std::exception& e){cout << e.what() << endl;}
}

在这里插入图片描述

  • 这段代码定义了一个名为 Func 的函数,该函数在一个无限循环中不断尝试分配大约1MB的内存
  • 由于没有适当的内存释放,这将最终导致内存耗尽,从而抛出 std::bad_alloc 异常
  • 在 main 函数中, Func 被调用并被包裹在一个 try-catch 块中,以捕获并处理可能抛出的异常。

然而,由于 std::bad_alloc 异常没有被直接捕获( catch 块中捕获的是 std::exception 的引用),所以实际上这段代码可能不会按预期工作,因为 std::bad_alloc 可能在到达 catch 块之前就已经导致程序终止

3. new和delete操作自定义类型

  • 在 C++ 里,new 和 delete 除了能操作基本数据类型,还可以操作自定义类型。

自定义类型是程序员自己定义的类型,像类、结构体等.

  • 以下是new 和 delete 操作基本数据类型的简单代码
#include <iostream>
using namespace std;
class Person
{
public:Person(const char* _name,int _age){name = _name;age = _age;cout << "Person 构造函数被调用,名字: " << name << ", 年龄: " << age;};~Person(){cout << "Person 析构函数被调用: " << name << ", 年龄: " << age;}private:const char *  name;//一个指向常量字符的指针类型int age;};
int main() {// 使用 new 创建 Person 对象Person* person = new Person("Alice", 25);// 使用 delete 释放对象内存delete person;return 0;
}

在这里插入图片描述

这段代码定义了一个 Person 类,包含姓名和年龄两个属性,还有构造函数和析构函数。在 main 函数里,使用 new 操作符创建一个 Person 对象,然后使用 delete 操作符释放对象的内存。

二、 operator new与operator delete函数(重点)

1. new和delete操作符与operator new和operator delete函数的关系

  • 前面我们讲到new和delete是用来动态申请和释放内存的操作符。比如,你想创建一个int类型的变量,就可以用new操作符来申请内存:
int* ptr = new int;
  • 这里的new操作符在底层会调用operator new全局函数来申请内存空间
    在这里插入图片描述

  • 当你不再需要这块内存时,就得用delete操作符释放它:

delete ptr;

delete操作符在底层会调用operator delete全局函数来释放内存空间
在这里插入图片描述

2. operator new函数的工作原理

  • operator new函数的作用是申请内存空间,它实际上是借助malloc函数来实现的

下面是它的工作步骤:

  • 尝试申请内存:调用malloc函数去申请指定大小的内存空间。
  • 检查申请结果:
    • 成功:若malloc申请内存成功,就直接返回这块内存的指针。
    • 失败:若malloc申请内存失败,会尝试执行用户设置的空间不足应对措施
    • 用户设置了应对措施:继续尝试申请内存。
    • 用户未设置应对措施:抛出std::bad_alloc类型的异常

下面是operator new函数的简化代码:

void *operator new(size_t size) {void *p;while ((p = malloc(size)) == 0) {if (用户设置的应对措施函数(size) == 0) {// 申请内存失败,抛出异常static const std::bad_alloc nomem;throw nomem;}}return p;
}

3. operator delete函数的工作原理

  • operator delete函数的作用是释放内存空间,它最终是通过free函数来实现的。

下面是它的工作步骤:

  • 检查指针是否为空:若传入的指针为空,直接返回,不做任何操作。
  • 释放内存:调用free函数释放这块内存空间。

下面是operator delete函数的简化代码:

void operator delete(void *pUserData) {if (pUserData == NULL)return;free(pUserData);
}
  • new操作符在底层调用operator new函数来申请内存空间,operator new函数又借助malloc函数来申请内存
  • delete操作符在底层调用operator delete函数来释放内存空间,operator delete函数最终通过free函数来释放内存
  • 若malloc申请内存失败,operator new函数会尝试执行用户设置的应对措施,若没有设置则抛异常

三、 定位new表达式(placement-new) (了解即可)

1. 定位 new 表达式的概念

一般而言,使用new操作符时,它会做两件事

  • 一是为对象分配内存;二是调用对象的构造函数来初始化这块内存。
  • 而定位 new 表达式有所不同,它是在已经分配好的原始内存空间里调用构造函数来初始化对象

2. 定位 new 表达式的使用格式

在C++中,定位 new 的语法如下

new (address) Type

在address指向的内存空间创建一个type类型的对象

new (address) Type[size]
  • 在address指向的内存空间创建一个type类型的对象,并且用size里的值来初始化对象

  • 这里的address得是一个指针,size是类型的初始化列表

3. 定位 new 表达式的使用场景

  • 在实际运用中,定位 new 表达式通常和内存池配合使用
  • 内存池分配的内存并未初始化,要是分配的是自定义类型的对象,就得使用定位 new 表达式来显式调用构造函数进行初始化
#include <iostream>int main() {// 分配一块原始内存char* rawMemory = new char[sizeof(int)];// 使用定位new在原始内存上构造一个int对象int* intPtr = new (rawMemory) int(42);// 输出构造的int对象的值std::cout << "Value of int object: " << *intPtr << std::endl;// 显式调用析构函数(对于基本类型,这一步不是必需的,但对于自定义类型是必需的)intPtr->~int();// 释放原始内存delete[] rawMemory;return 0;
}
#include <iostream>class MyClass {
public:MyClass(int value) : data(value) {std::cout << "Constructor called with value: " << data << std::endl;}~MyClass() {std::cout << "Destructor called for value: " << data << std::endl;}
private:int data;
};int main() {// 分配一块原始内存char* rawMemory = new char[sizeof(MyClass)];// 使用定位new在原始内存上构造一个MyClass对象MyClass* myObjPtr = new (rawMemory) MyClass(10);// 显式调用析构函数myObjPtr->~MyClass();// 释放原始内存delete[] rawMemory;return 0;
}    

四、malloc/free和new/delete的区别

1. 相同点

malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。

  • 不过,他们的不同点是

2. 不同点

一个是函数,一个是操作符

  • malloc/free 是 C 语言的库函数:需要包含头文件 <stdlib.h>,用函数的方式调用(比如 malloc(size))。
  • new/delete 是 C++ 的操作符:是 C++ 语言内置的功能,用法更简洁(比如 new int)

new 会 “初始化”,malloc 不会

malloc 申请的内存是 “脏的”:里面可能是随机的垃圾值。比如

int* p = (int*)malloc(sizeof(int));  // *p 的值不确定,可能是任意数
  • new 申请的内存会被 “初始化”
    • 对内置类型(如 int、double),new int 不会初始化,但 new int() 会初始化为 0;
    • 对自定义类型(如类),new 会自动调用构造函数,完成对象的初始化(比如给成员变量赋值)。

空间大小:new 自动计算,malloc 要手动算

  • malloc 需要自己算大小:必须用 sizeof 计算需要的字节数,比如申请 5 个 int 的空间:
int* p = (int*)malloc(5 * sizeof(int));  // 手动算 5*4=20 字节
  • 如果算错(比如漏掉 sizeof 或乘错数),就会出 bug
  • new 自动知道要多大:直接写类型和数量即可,比如:
int* p = new int[5];  // 自动申请 5 个 int 的空间,不用算字节数

new 不用强转,malloc 需要

malloc 返回 void* 指针:使用时必须强制转换类型,比如:

int* p = (int*)malloc(sizeof(int));  // 必须强转成 int*

new 直接返回对应类型的指针:比如 new int 直接返回 int*,不需要强转:

int* p = new int;  // 直接是 int* 类型,不用强转

错误处理:malloc 返 NULL,new 抛异常

  • malloc 申请失败返回 NULL:必须检查是否为 NULL,否则解引用(比如 *p)会导致程序崩溃:
int* p = (int*)malloc(sizeof(int));
if (p == NULL) {  // 必须判空!// 处理内存不足的情况
}
  • new 申请失败会抛出异常:默认会抛出 std::bad_alloc 异常,需要用 try-catch 捕获(或者用 new(nothrow) 版本返回 NULL,但不常用):
try {int* p = new int;  // 失败会抛异常,不会返回 NULL
} catch (std::bad_alloc& e) {// 处理异常
}

自定义类型:new/delete 会 “照顾” 对象,malloc/free 不会

malloc/free 只负责搬砖:

  • malloc 只会分配一块足够大的内存,但不会调用类的 构造函数(比如初始化成员变量);
  • free 只会释放内存,但不会调用类的 析构函数(比如释放对象内部申请的资源)。
    这样会导致对象没被正确初始化或清理,造成错误或内存泄漏。

new/delete 会 “盖房子” 和 “拆房子”:

  • new 分配内存后,会自动调用类的构造函数,初始化对象(比如给成员变量赋值);
  • delete 释放内存前,会自动调用类的析构函数,清理对象内部的资源(比如释放成员指针指向的内存)。

以上就是这篇博客的全部内容,下一篇我们将继续探索C++中模板初阶更多精彩内容。

我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的C++知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12880513.html?spm=1001.2014.3001.5482

非常感谢您的阅读,喜欢的话记得三连哦

在这里插入图片描述


http://www.hkcw.cn/article/yTgXFbTgxC.shtml

相关文章

【2025年最新版】Java JDK安装、环境配置教程 (图文非常详细)

文章目录 【2025年最新版】Java JDK安装、环境配置教程 &#xff08;图文非常详细&#xff09;1. JDK介绍2. 下载 JDK3. 安装 JDK4. 配置环境变量5. 验证安装6. 创建并测试简单的 Java 程序6.1 创建 Java 程序&#xff1a;6.2 编译和运行程序&#xff1a;6.3 在显示或更改文件的…

【Linux系统】从 C 语言文件操作到系统调用的核心原理

文章目录 前言lesson 15_基础IO一、共识原理二、回顾C语言接口2.1 文件的打开操作2.2 文件的读取与写入操作2.3 三个标准输入输出流 三、过渡到系统&#xff0c;认识文件系统调用3.1 open 系统调用1. 比特位标志位示例 3.2 write 系统调用1. 模拟实现 w 选项2. 模拟实现 a 选项…

JavaSwing之--JTextField

JavaSwing之–JTextField JTextField 是一个允许编辑单行文本的轻量级组件&#xff0c;它提供了一系列的构造方法和常用方法用来编写可以存储文本的文本框满足程序功能的需求。 以下在简要介绍常用构造方法、普通方法后详解各种方法的应用及举例。 一、构造方法 方法名称功…

Windows系统之VHD安装

环境准备 工具说明Dism部署系统、提取和转换系统镜像等等&#xff0c;还有很多功能大家可以自行探索。这里只用到Dism的部署系统功能。 Releases Chuyu-Team/Dism-Multi-language GitHubbcdedit.exe自带工具 C:\Windows\System32\bcdedit.exe 创建虚拟磁盘 首先右键点击我…

解决Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field ‘com.sun.tools.javac.tre

问题描述 在更新自建基础项目过程中&#xff0c;compile、install报错。 Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field com.sun.tools.javac.tree.JCTree qualid 解决方案 问题原因是Lombok &#xff0c;与 JDK 21 兼容的最低 Lombok 版本是…

【C++】二叉搜索树 - 从基础概念到代码实现

&#x1f4cc; 个人主页&#xff1a; 孙同学_ &#x1f527; 文章专栏&#xff1a;C &#x1f4a1; 关注我&#xff0c;分享经验&#xff0c;助你少走弯路 文章目录 1. 二叉搜索树的概念2. 二叉搜索树的性能分析3. 二叉搜索树的插入4. 二叉搜素树的查找5. 二叉搜索树的删除6.二…

C++之类和对象基础

⾯向对象三⼤特性&#xff1a;封装、继承、多态 类和对象 一.类的定义1. 类的定义格式2.类域 二.实例化1.对象2.对象的大小 三.this指针 在 C 的世界里&#xff0c;类和对象构成了面向对象编程&#xff08;Object-Oriented Programming&#xff0c;OOP&#xff09;的核心框架&…

报错java: java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport does not ...解决方法

在运行项目时出现java: java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field com.sun.tools.javac.tree.JCTree qualidzz这样的报错 解决方法 1.第一步&#xff1a;在pom文件中将lombok的版本改成最新的 此时1.18.34是新…

2025-03-12 Python深度学习1——安装Anaconda与PyTorch库

文章目录 1 配置 Anaconda1.1 下载1.2 安装1.3 配置环境变量1.4 检查安装 2 安装 PyTorch 库2.1 创建 DL 环境2.2 安装/升级 CUDA2.3 配置环境变量2.4 安装 Pytorch 库方法一&#xff08;不稳定&#xff09;方法二&#xff08;推荐&#xff09; 2.5 检查安装 3 Pycharm Communi…

C++ 关联式容器:map,multimap,set,multiset

目录 引言 一、关联式容器概述 1.1 与序列式容器的区别 1.2 底层结构 二、set容器详解set介绍 2.1 set的特性 2.2 set的模板参数 2.3 set的常用接口 2.4 set使用示例 三、map容器详解map介绍 3.1 map的特性 3.2 map的模板参数 3.3 map的常用接口 3.4 map使用示例 …

从零开始配置Qt+VsCode环境

从零开始配置QtVsCode环境 文章目录 从零开始配置QtVsCode环境写在前面扩展安装及配置Qt Configure配置 VsCode创建Qt工程VsCodeQMakeMinGwVsCodeQMakeMsvcVsCodeCMakeMinGwVsCodeCMakeMsvcQtCreatorQMakeMinGw->VsCodeQtCreatorQMakeMsvc->VsCodeQtCreatorCMakeMinGw-&g…

Matlab/Simulink - BLDC直流无刷电机仿真基础教程(一) - 三相逆变器的搭建

Matlab/Simulink - BLDC直流无刷电机仿真基础教程&#xff08;一&#xff09; - 三相逆变器的搭建 前言一、BLDC电机六步换相简明控制原理二、Simulink中BLDC电机模块的机械连接三、三相逆变电路的搭建四、仿真参数设置与仿真结果验证五、补充内容参考链接 前言 本系列文章分享…

Lapce:一款用 Rust 编写的快速且强大的代码编辑器

Lapce&#xff08;IPA&#xff1a;/lps/&#xff09;是一个使用纯 Rust 编写的开源代码编辑器。通过利用 OpenGL 渲染 GUI&#xff0c;以及 Rust 提供的性能&#xff0c;采用Xi-Editor的Rope Science设计&#xff0c;可实现闪电般的快速计算。 Stars 数35888Forks 数1113 主要…

SpringBoot启动后初始化的几种方式

目录 一、静态代码块 二、构造方法 三、PostConstruct 四、InitializingBean 接口 五、 Bean 注解中的 initMethod 六、 CommandLineRunner 接口 七、ApplicationRunner 接口 八、EventListener事件 九、SmartInitializingSingleton接口 十、ApplicationListener接口…

【MySQL课程学习】:MySQL安装,MySQL如何登录和退出?MySQL的简单配置

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;MySQL课程学习 &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 MySQL在Centos 7环境下的安装&#xff1a; 卸载…

Node.js下载安装及环境配置教程(保姆级教程)

一、安装程序 &#xff08;安装包放在文章最后需要的友友可自取哦&#xff09; &#xff08;1&#xff09;下载完成后&#xff0c;双击安装包&#xff0c;开始安装Node.js &#xff08;2&#xff09;此位置可修改为自己的安装路径&#xff0c;修改完后点击next &#xff08;3…

com.mysql.cj.jdbc.exceptions.CommunicationsException Communications link failure 问题解决

前言: 一般这个报错大多是网络原因导致的&#xff0c;确保你不是网络问题再往下看 问题 在一个方法上&#xff08;该方法非常复杂执行时间长&#xff09;加了 Transactional(rollbackFor Exception.class)后出现了如下图所示的错误 解决&#xff1a; 经过排查并非网络问…

【解决方案】CloudFront VPC Origins 实践流程深入解析 —— 安全高效架构的实战之道

目录 引言一、VPC Origins 的核心价值&#xff08;一&#xff09;安全性提升&#xff08;二&#xff09;运维效率优化&#xff08;三&#xff09;成本节约&#xff08;四&#xff09;全球分发能力的保留 二、VPC Origins 的架构解析&#xff08;一&#xff09;流量路径设计&…

MySQL性能调优(三):MySQL中的系统库(sys系统库、information_schema)

文章目录 MySQL性能调优数据库设计优化查询优化配置参数调整硬件优化 MySQL中的系统库1.3.sys系统库1.3.1.sys使用须知1.3.2.sys系统库使用1.3.3.查看慢SQL语句慢在哪里1.3.4.小结 1.4.information_schema1.4.1.什么是information_schema1.4.2.information_schema表分类Server层…

MySQL的详细使用教程

目录 1. 连接到MySQL服务器2. 创建和删除数据库2-1创建数据库2-2删除数据库 3. 数据表操作3.1 选择数据库3.2 创建数据表3.3 查询数据表3.4 修改数据表3.5 删除数据表 4. 数据内容操作4.1数据操作1. 插入数据2. 查询数据&#xff08;1&#xff09;like模糊查询&#xff08;%表示…