文章目录
-
- 上期回顾
- C/C++ 内存管理
-
- 一、本讲核心主线
- 二、C/C++ 内存分布
- 三、典型变量位置判断
- 四、回顾一下C 语言动态内存管理
-
- 1. `malloc`
- 2. `calloc`
- 3. `realloc`
- 4. `free`
- 五、C++ 动态内存管理:`new/delete`⭐️⭐️⭐️
-
- 1. 操作内置类型
- 2. 操作自定义类型
- **知识点总结:**
- 六、`operator new` 和 `operator delete`
- 七、`new/delete` 实现原理
-
- 1. 内置类型
- 2. 自定义类型的 `new` 原理
- 3. 自定义类型的 `delete` 原理
- 4. `new T[N]` 原理
- 5. `delete[]` 原理
- 八、定位 new:`placement new`⭐️⭐️
- 九、`malloc/free` 和 `new/delete` 的区别
- 十、易错点汇总
- 下期预告
- 模版初阶
- 结语
◆ 博主: @此生决int
分享编程知识!深挖底层原理!持续原创更新!
热门专栏: 深入理解 C++系列|算法系列 快速复习系列 |Java 速通系列
📌 本系列知识点前后关联较强,建议按照专栏顺序阅读哦
本系列主要面向的阅读人群
1,学完 C 语言,准备系统学习 C++ 的同学
2,已经学习过 C++,希望重新梳理知识体系的同学
3,想深入理解 C++ 设计思想与底层原理,而不仅仅停留在语法层面的同学
不止教你会写代码,更带你理解代码为什么这样设计!
—
上期回顾
hello,大家好,这里是此生决int,上一期我们终于结束了面向对象的语音C++的一个重要组成部分——类和对象,今天我们就一起来看看变量在C++里面是怎么存储的吧,看看他相较于C语言有哪些改进之处!
C/C++ 内存管理
一、本讲核心主线
这一讲主要解决三个问题:
一句话总结:
malloc/free 只负责堆空间申请和释放;new/delete 除了申请和释放空间,还会对自定义类型自动调用构造函数和析构函数。
二、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);
}
重点结论:
| globalVar | 数据段 / 静态区 | 全局变量 |
| staticGlobalVar | 数据段 / 静态区 | 静态全局变量 |
| staticVar | 数据段 / 静态区 | 静态局部变量 |
| localVar | 栈 | 普通局部变量 |
| num1 | 栈 | 局部数组整体在栈上 |
| char2 | 栈 | 局部字符数组整体在栈上 |
| *char2 | 栈 | 数组元素在数组内部 |
| pChar3 | 栈 | 局部指针变量本身在栈上 |
| *pChar3 | 代码段 / 常量区 | 指向字符串常量 |
| ptr1 | 栈 | 局部指针变量本身在栈上 |
| *ptr1 | 堆 | malloc 申请的空间 |
最容易错的是:(后面学string类如果不懂那就是没有理解这里!!!)
char char2[] = "abcd"; // 字符数组,内容拷贝到栈上
const char* pChar3 = "abcd"; // 指针在栈上,字符串常量在常量区
图示: 
四、回顾一下C 语言动态内存管理
C 语言中常用:
malloc
calloc
realloc
free
1. malloc
int* p = (int*)malloc(sizeof(int) * 4);
特点:
- 在堆上申请指定字节数的空间。
- 不初始化申请到的空间。
- 返回 void*,C++ 中需要强转。
- 申请失败返回 NULL。
注释1 一般都要强转,但C语言存在隐式类型转换,C++不支持,所以必须这样写
int* p = (int*)malloc(sizeof(int) * 4);
2. calloc
int* p = (int*)calloc(4, sizeof(int));
特点:
- 申请 4 个 int 大小的空间。
- 会把空间初始化为 0。
3. realloc
int* p2 = (int*)realloc(p1, sizeof(int) * 10);//注释1
特点:
- 调整原动态内存块大小。
- 可能原地扩容,也可能重新开空间并拷贝旧数据。
- 返回新地址。
注意:
int* p3 = (int*)realloc(p2, sizeof(int) * 10);
free(p3);
这里不需要再 free(p2),因为 realloc 成功后应以返回的新指针为准。 注释1 这里可以写成这样吗?
p1 = (int*)realloc(p1, sizeof(int) * 10);//注释1
不行! realloc 如果失败,会返回 NULL,但不会释放原来的内存。如果直接使用:
p = realloc(p, size);
会导致原指针被 NULL 覆盖,从而丢失原内存地址,最终造成内存泄漏。
4. free
free(p);
特点:
- 释放堆空间。
- 只释放空间,不会调用析构函数。注释1
注释1 例如,free(类类型);他只会释放对应的空间,而不会调用类类型的析构函数。总之,只需要记住:malloc/free、new/delete、new[]/delete[] 一一对应,不能混用。就没有什么问题!(具体的后面标题:2,操作自定义类型会讲到)
五、C++ 动态内存管理:new/delete⭐️⭐️⭐️
C++ 中推荐使用:
new
delete
new[]
delete[]
1. 操作内置类型
int* p1 = new int;//int*p1,定义一个int指针,new int堆上申请一个int大小的空间,但是没有初始化!!!
int* p2 = new int(10);
int* p3 = new int[3];
delete p1;
delete p2;
delete[] p3;
注意匹配:一定要匹配使用。
| new | delete |
| new[] | delete[] |
2. 操作自定义类型
class A
{
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
};
A* p = new A;
delete p;
核心区别:
- new A:申请空间 + 调用构造函数。
- delete p:调用析构函数 + 释放空间。
而:
A* p = (A*)malloc(sizeof(A));
free(p);
只会申请和释放空间,不会调用构造和析构。 我的理解: A* p = new A; 既申请了空间,又创建了对象,调用了构造函数, delete p; 他既调用了析构函数,又释放了空间, A* p = (A*)malloc(sizeof(A)); 和free§; 只是申请了空间,没有创建对象,也没有调用构造和析构函数!
知识点总结:
free 只负责释放 malloc 申请的那块原始内存,不会调用对象的析构函数,因此对象内部管理的资源不会被自动释放。 new/delete 不仅负责内存申请和释放,还负责构造函数和析构函数的调用,所以 C++ 中管理类对象通常使用 new/delete。 同时需要保证 malloc/free、new/delete、new[]/delete[] 一一对应,不能混用。
六、operator new 和 operator delete
重点区分:
| new | 操作符,用户使用 |
| delete | 操作符,用户使用 |
| operator new | 全局函数,底层负责申请空间 |
| operator delete | 全局函数,底层负责释放空间 |
new 的底层会调用 operator new。 delete 的底层会调用 operator delete。
大致关系:
new -> operator new -> malloc
delete -> operator delete -> free
operator new 申请失败时通常会抛出 bad_alloc 异常,而不是像 malloc 那样返回 NULL。
七、new/delete 实现原理
1. 内置类型
对于内置类型,差别相对较小。
int* p1 = (int*)malloc(sizeof(int));
int* p2 = new int;
主要区别:
- malloc 失败返回 NULL。
- new 失败抛异常。
- new 写法更简单,不用手动计算字节数和强转。
2. 自定义类型的 new 原理
A* p = new A(10);
步骤:
3. 自定义类型的 delete 原理
delete p;
步骤:
4. new T[N] 原理
A* p = new A[10];
步骤:
5. delete[] 原理
delete[] p;
步骤:
八、定位 new:placement new⭐️⭐️
定位 new 的作用:
在已经分配好的原始内存空间上调用构造函数初始化对象。
格式:
new(place_address) Type;
new(place_address) Type(initializer–list);//Type(初始化列表);
示例:
A* p = (A*)malloc(sizeof(A));//申请空间
new(p) A(10);//指定空间创建对象,并调用构造函数
p->~A();//指定空间调用析构函数
free(p);//释放空间
解释:
使用场景(了解)
常和内存池配合使用。内存池只提供原始内存,自定义类型对象还需要显式构造。
九、malloc/free 和 new/delete 的区别
共同点:
都是从堆上申请空间,都需要用户手动释放。
区别:
| 本质 | 函数 | 操作符 |
| 空间大小 | 手动计算字节数 | 根据类型自动计算 |
| 返回类型 | void*,C++ 中需强转 | 返回对应类型指针 |
| 初始化 | malloc 不初始化,calloc 清零 | new 可以初始化 |
| 失败处理 | 返回 NULL | 抛异常 |
| 自定义类型 | 不调用构造 / 析构 | 自动调用构造 / 析构 |
| 数组申请 | 手动算总大小 | new[] 指定对象个数 |
| 释放方式 | free | delete / delete[] |
最重要区别:
对自定义类型,new/delete 会调用构造函数和析构函数,malloc/free 不会。
十、易错点汇总
下期预告
模版初阶
结语
本文到此结束,感谢大家的阅读!如果觉得本文对你有所帮助,欢迎点赞、收藏、关注,也欢迎在评论区一起交流讨论。 也欢迎订阅我的 深入理解 C++系列:从语法入门到底层原理,系统掌握现代 C++ 算法系列:从入门到精通,蓝桥杯、ACM、LeetCode 与面试算法全路线 快速复习系列:知识梳理、查漏补缺,考前冲刺必备 Java 速通系列:已学 C 语言,快速上手 Java,轻松备战期末考试
愿每一次敲下键盘,都比昨天更进一步!
愿每一行代码落下,都让未来多一种可能!




