文章目录
- 回顾C语言中的内存管理
- [C++的内存管理(new delete)](#C++的内存管理(new delete))
- [malloc /free和new/delete的区别](#malloc /free和new/delete的区别)
- 定位new(placement_new)
回顾C语言中的内存管理
内存四大区
|------------------------------------------------------------------------------------------------------------|
| 1 栈区:主要存放局部变量,函数参数等。 2堆区:malloc,calloc ,realloc开辟的空间。 3 常量区:字符串常量,const常量,只读不改。 4 静态区:用于存储全局变量,static 静态变量 |
一道经典题目
如下
cpp
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. 选择题:
选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
globalVar在哪里?____
staticGlobalVar在哪里?____
staticVar在哪里?____
localVar在哪里?____
num1 在哪里?____
char2在哪里?____
*char2在哪里?___
pChar3在哪里?____
*pChar3在哪里?____
ptr1在哪里?____
*ptr1在哪里?____
答案如下图

malloc ,calloc和realloc的区别
1malloc
|----------------------------------------------------|
| malloc 的声明为:void* malloc(size_t) 分配指定大小的内存块,不初始化。 |
2calloc
|-------------------------------------------------------------------------------------------------------|
| calloc 的声明为:void* calloc(size_t num,size_t size ),参数为变量的数量和每个变量的大小。 相较于malloc()不会初始化,calloc()会初始化成0。 |
3realloc
|---------------------------------------------------------------------------------------------------------------------------------------|
| realloc的声明为:void* realooc(void* ptr,size_t new_size)重新调整之前分配的内存块大小,(可能会移动内存),会保留原有的数据,对新增的数据不初始化。也可以用于缩小内存。当给ptr传nullptr时等同于malloc。 |
C++的内存管理(new delete)
new
作用
在堆区动态开辟内存,自动初始化,返回对应类型指针。
语法
对于内置类型
cpp
// 单个变量
int* p = new int;
int* p2 = new int(10); // 初始化值10
// 数组
int* arr = new int[5];
// 释放
delete p;
delete[] arr;
对于自定义类型
cpp
int main()
{
Date* d1 = new Date;
Date* d2 = new Date[3];//调用默认构造初始化
Date* d3 = new Date[3]{ Date(2026,5,20),Date(2026,5,21),Date(2025,6,9) };//用匿名对象拷贝构造初始化
Date* d4 = new Date[3]{ {2026,5,20},{2026,5,21}, {2025,6,9} };//用隐式类型转换初始化
delete d1;
delete[] d2;
delete[]d3;
delete[] d4;
return 0;
}
new的优势
|----------------------------------------------------------------------------------------------|
| new 的优势主要在于可以调用构造函数初始化,这样就使我们定义自定义类型时方便了很多。比如在链表中我要创建一个节点并初始化,只需要一个new就搞定了。而不是先malloc再手动初始化。 |
判断new是否成功
|--------------------------------------------|
| 需要捕获异常来判断,现在我们先不用了解这个,后面再说。其实一般也不会失败,不用担心。 |
new和delete的底层原理
1. new(加强版malloc)
new 操作符在底层实际上分为两个步骤:
- 调用 operator new 函数申请内存空间:operator new 的底层实现就是 malloc,负责在堆上分配指定大小的内存。
- 在申请的空间上调用构造函数:对于自定义类型,new 会自动调用其构造函数完成对象的初始化;对于内置类型,如果提供了初始值,也会进行相应的初始化。
2. delete
delete 操作符的执行过程也分为两步:
- 调用析构函数:在要释放的对象空间上执行析构函数,完成对象内部资源的清理工作(如关闭文件、释放子对象等)。
- 调用 operator delete 释放内存:operator delete 的底层实现就是 free(),负责将内存归还给系统。
3. new T[N]
new T[N] 用于动态分配对象数组,其底层原理为:
- 调用 operator new 申请连续内存:一次性申请能够容纳 N 个对象的内存空间。
- 调用 N 次构造函数:对数组中的每一个元素依次调用构造函数进行初始化(对于内置类型,若未指定初值,则保持未初始化状态)。
4. delete[]
delete[] 用于释放对象数组,其执行流程为:
- 执行 N 次析构函数:按照数组元素的逆序(或实现定义的顺序)依次调用每个对象的析构函数,确保每个对象的资源都被正确清理。
- 调用 operator delete 释放整块内存:最后通过 operator delete 释放之前申请的连续内存空间。
5. new 与 delete 必须配对使用
重要原则:new 和 delete、new T[N] 和 delete[] 必须严格配对使用,否则会导致一些错误,常见错误包括:
- new 配 free():不会调用析构函数,可能导致资源泄漏(如文件句柄未关闭、子对象内存未释放)。
- new T[N] 配 delete:由于内存布局信息不一致,通常会导致程序崩溃(尤其是自定义类型带有析构函数时)。
错误示例及后果
示例1:new 配 free()
cpp
Date* d1 = new Date;
free(d1); // 错误:未调用析构函数
- 后果:程序不会立即崩溃,但析构函数未被调用,可能导致内存泄漏或资源泄漏。在 VS 中会产生警告。
示例2:new T[N] 配 delete
cpp
Date* d1 = new Date[10];
delete d1; // 错误:应使用 delete[]
- 后果 :程序通常直接崩溃。原因是编译器在分配数组时可能在对象内存前存储了数组大小等信息,而 delete 无法正确识别该布局,导致开辟内存的位置和释放内存的位置不一致。

原理如下图:delete直接在中间释放内存

malloc /free和new/delete的区别
共同点
|--------------------|
| 都需要往堆上申请空间,都需要手动释放 |
不同点
1 使用方面:
|-----------------------------------------|
| 1 malloc()和free()是函数,new和delete是操作符。。。。 |
|---------------------------------|
| 2 malloc申请的空间不会初始化,new可以初始化。。。。 |
|-----------------------------------------------------------------------|
| 3 malloc申请空间的大小需要手动传递,而new后面直接跟类型即可,如果要开辟多个对象,后面加上[ ] 指定对象个数即可。 。。。 |
|-----------------------------------------|
| 4返回类型是void*需要强转,而new不需要,因为new后面跟的就是类型。 |
2 底层功能不同:
|-----------------------------------------|
| malloc申请空间失败会返回NULL,因此需要判空,而new则需要捕获异常。 |
|-----------------------------------------------------------------------------------------------------|
| ###较重要的一点是:申请自定义类型的时候,malloc和free只会开辟空间,不会调用构造和析构。这就非常挫了。而new会在开辟空间后调用构造函数初始化,delete会在释放空间之前调用析构函数。 |
new和delete的底层功能示意
以栈Stack为例

定位new(placement_new)
作用
|----------------------------------------------------------------------------------------------|
| 主要就是用来显示调用构造函数的。构造函数不是能自动调用吗?这其实是用于内存池中的对象的,因为从内存池中开辟的空间不会初始化。。。。而析构函数是可以直接调用的,但构造就需要借助定位new |
格式
使用格式是: new (place_adrress ) type (initializer_list) ,其中place_adrress是指针的意思,initializer_list是参数的意思。
一个使用场景(这里的operator new和malloc()是差不多的)。

内存池
内存池应该是后面才会学习的东西,我们现在先来简单了解一下。
简单来说内存池就是堆给的一块封地 ,在这块封地上,归某个诸侯(某个需要分配内存的进程)所统治 ,别的地方的人人无权干涉。
那这样有什么作用呢?是为了提高内存申请的效率(有些时候我们需要高频申请释放内存)。当有多个进程同时向堆申请内存时,难免需要排队啥的,效率会比较低下,把堆也搞得比较烦。而堆就说:这样吧,你跟我说你大概需要多大的内存,我直接一次性分给你。
大概的逻辑就是这样,具体的细节还需要后面的学习。