一、上期回顾
掌握函数模板、类模板、泛型编程、模板特化,理解了 STL 容器能适配任意类型的底层原因。今天攻坚C++ 内存管理,搞定 new/delete、内存分区、野指针、内存泄漏四大核心痛点。
二、C/C++ 程序内存五大分区
程序运行时内存划分为 5 块,面试必背:
- 栈区:局部变量、函数形参、栈对象;自动分配自动释放
- 堆区:new/malloc 开辟的空间;手动申请手动释放
- 全局 / 静态区:全局变量、static 静态变量;程序全程存在
- 常量区:字符串常量、const 全局常量;只读不可修改
- 代码区:存放二进制程序指令,只读
三、malloc/free 与 new/delete 核心区别
1. C 语言 malloc/free
-
只分配原始内存,不调用构造、析构函数
-
需要强转类型、手动计算字节大小
-
不会初始化内存
int* p = (int*)malloc(sizeof(int));
free(p);
2. C++ new/delete
-
分配内存 + 自动调用构造函数
-
释放内存 + 自动调用析构函数
-
无需计算大小、无需强转
-
支持数组形式 new [] /delete []
int* p = new int;
delete p;// 数组开辟
int* arr = new int[5];
delete[] arr;
核心区别总结
- new 自动调用构造,malloc 只开空间
- new 无需强转、自动计算大小
- delete 自动调用析构,free 直接回收内存
- 数组必须配对
new[]和delete[]
四、new 的三种使用形式
// 1. 普通开辟单个变量
int* p1 = new int;
// 2. 开辟并初始化
int* p2 = new int(100);
// 3. 开辟数组
int* p3 = new int[5];
五、内存泄漏成因与规避
什么是内存泄漏
堆空间用new申请后,没有 delete 释放,程序不退出内存一直占用。
常见泄漏场景
- 函数内 new,没有返回也没有释放
- 指针重新赋值,旧地址丢失无法释放
- 类中堆成员,析构函数没写 delete
避坑原则
谁申请,谁释放;成对使用 new/delete
六、野指针成因与危害
野指针定义
指向非法未知内存的指针,没有有效指向。
产生原因
- 指针未初始化,随机地址
- 指针指向栈变量,变量销毁后指针悬空
- 内存释放后,未把指针置空
规避方法
-
定义指针初始化为
nullptr -
释放内存后立刻置空
-
不访问悬空指针
// 规范写法
int* p = new int;
delete p;
p = nullptr; // 置空,避免野指针
七、内存经典错误示范
错误 1:重复释放
int* p = new int;
delete p;
delete p; // 崩溃,重复释放
错误 2:数组不匹配 delete []
int* arr = new int[5];
delete arr; // 错误,必须 delete[]
错误 3:野指针访问
int* p;
*p = 10; // 未初始化,野指针崩溃
八、构造析构与 new/delete 联动演示
#include <iostream>
using namespace std;
class Test
{
public:
Test(){cout << "构造函数调用" << endl;}
~Test(){cout << "析构函数调用" << endl;}
};
int main()
{
// 栈对象:自动构造、自动析构
Test t1;
// 堆对象:new触发构造,delete触发析构
Test* t2 = new Test;
delete t2;
return 0;
}
运行结果:
构造函数调用
构造函数调用
析构函数调用
析构函数调用
九、今日核心总结
- 内存五分区:栈、堆、全局静态、常量、代码区
- new/delete 比 malloc/free 多了构造析构调用
- 数组必须严格配对
new[]和delete[] - 内存泄漏:new 不 delete;野指针:未初始化 / 悬空指针
- 开发规范:指针初始化为空、释放后置空、成对申请释放
十、课后练习
- 用 new 开辟单个 int 并初始化,delete 释放
- 尝试不 delete 观察内存泄漏逻辑
- 定义未初始化指针,理解野指针危害