一、为什么需要 new/delete?
c++的内存分配主要是栈内存和堆内存,两者的核心区别决定了new/delete的必要性:
| 内存区域 | 分配 / 销毁方式 | 大小限制 | 生命周期 | 示例 |
| 栈内存 | 编译器自动分配 / 销毁 | 小(通常几 MB) | 随作用域结束销毁 | int num = 10;、Student s; |
| 堆内存 | 手动用 new 分配 /delete 销毁 | 大(受物理内存限制) | 手动释放前一直存在 | int* p = new int;、Student* s = new Student(); |
栈内存的核心痛点:
- 栈内存大小有限,创建大数组 / 大对象时会栈溢出;
- 栈对象生命周期固定,无法在函数结束后保留数据;
- 动态数据结构(链表、树)需要灵活的内存分配,栈内存无法满足。
new/delete的核心作用:手动管理堆内存,解决栈内存的局限性。
二、new的核心用法(分配堆内存)
new是 C++ 的动态内存分配运算符,作用是向操作系统申请指定大小的堆内存,返回指向该内存的指针。
1.基本语法(四种核心场景)
| 场景 | 语法示例 | 说明 |
| 分配单个基本类型变量 | int* p = new int; | 申请 4 字节堆内存,返回int*指针;内存值为随机数(未初始化) |
| 分配单个变量并初始化 | int* p = new int(10); | 申请 4 字节堆内存,初始化为 10;支持所有基本类型(char* c = new char('a');) |
| 分配数组 | int* arr = new int[5]; | 申请 20 字节堆内存(5 个 int),返回指向第一个元素的指针;元素值随机 |
| 分配数组并初始化(C++11+) | int* arr = new int[5]{1,2,3,4,5}; | 申请 20 字节堆内存,元素初始化为指定值;未指定的元素默认初始化为 0(比如new int[5]{1,2} → [1,2,0,0,0]) |
2. 分配自定义类型(类、结构体)
#include <iostream>
#include <string>
using namespace std;
class Person {
public:
string name;
int age;
// 构造函数
Person() : name("未知"), age(0) {
cout << "无参构造调用" << endl;
}
Person(string n, int a) : name(n), age(a) {
cout << "带参构造调用" << endl;
}
};
int main() {
// 1. 分配单个对象(调用构造函数)
Person* p1 = new Person(); // 无参构造
Person* p2 = new Person("张三", 20); // 带参构造
// 2. 分配对象数组(调用无参构造)
Person* pArr = new Person[2]; // 2个对象,各调用一次无参构造
return 0;
}
三、delete 的核心用法(释放堆内存)
delete是 C++ 的内存释放运算符,作用是将堆内存归还给操作系统,必须和 new 配对使用。
1. 基本语法(必须和new严格配对)
| new 的用法 | delete 的对应用法 | 说明 |
new 类型(单个变量) | delete 指针; | 释放单个变量的堆内存;释放后指针变为野指针 |
new 类型[长度](数组) | delete[] 指针; | 释放数组的堆内存;必须加 [ ],否则内存泄漏(自定义类型还会导致析构函数不调用) |
2. 完整示例(new/delete 配对)
#include <iostream>
using namespace std;
int main() {
// 1. 单个变量:new → delete
int* p = new int(10);
cout << *p << endl; // 10
delete p; // 释放内存
p = nullptr; // 置空,避免野指针
// 2. 数组:new[] → delete[]
int* arr = new int[5]{1,2,3,4,5};
for (int i = 0; i < 5; i++) {
cout << arr[i] << " "; // 1 2 3 4 5
}
delete[] arr; // 释放数组(必须加[])
arr = nullptr;
// 3. 自定义类型:new → delete
class Person {
public:
~Person() { cout << "析构函数调用" << endl; }
};
Person* pObj = new Person();
delete pObj; // 释放时自动调用析构函数
pObj = nullptr;
return 0;
}
delete 之后为什么要置空指针:本质是为了避免野指针导致的未定义行为——delete 仅释放堆内存,但不会改变指针本身的值,指针仍指向已释放的内存地址,此时的指针就是 “野指针”,后续误操作会引发程序崩溃、数据错乱等严重问题。
四、new/delete 的核心规则(面试必记)
1. 严格配对原则(最核心)
new↔delete:一对一,少删内存泄漏,多删程序崩溃;new[]↔delete[]:必须加[],否则:- 基本类型数组:大概率内存泄漏(操作系统可能未检测);
- 自定义类型数组:析构函数仅调用第一个对象,其余对象内存泄漏。
2. 野指针规避(面试高频)
- 释放内存后,必须将指针置空(
p = nullptr); - 空指针释放合法:
delete nullptr;(编译器无操作,无需额外判断)。
3. 内存泄漏的核心场景(避坑)
内存泄漏:堆内存未释放且指针丢失,导致内存永久占用,常见场景:
// 场景1:分配后未释放
void func1() {
int* p = new int(10);
// 无delete → 函数结束后指针p销毁,堆内存泄漏
}
// 场景2:指针重赋值,丢失原内存地址
void func2() {
int* p = new int(10);
p = new int(20); // 原10的内存地址丢失,泄漏
delete p; // 仅释放20的内存
}
// 场景3:数组释放漏写[]
void func3() {
int* arr = new int[5];
delete arr; // 错误:应delete[] arr → 内存泄漏
}
4. new 的失败处理(进阶)
- 早期 C++:new 分配失败返回
NULL; - 现代 C++:new 分配失败抛出
bad_alloc异常(可捕获):
#include <iostream>
#include <new> // 包含bad_alloc
using namespace std;
int main() {
try {
// 申请超大内存,触发失败
int* p = new int[1000000000000];
delete[] p;
} catch (bad_alloc& e) {
cout << "内存分配失败:" << e.what() << endl;
}
return 0;
}
五、new/delete vs malloc/free(面试高频对比)
C++ 兼容 C 的malloc/free,但 new/delete 更适合 C++,核心区别:
| 维度 | new/delete | malloc/free |
| 本质 | C++ 运算符 | C 函数 |
| 构造 / 析构 | 自动调用(自定义类型) | 仅分配 / 释放内存,不调用 |
| 语法 | new int(10)(直接初始化) | malloc(4)(需手动计算字节数)+ memset初始化 |
| 返回值 | 直接返回对应类型指针(无需强制转换) | 返回void*,需强制转换(int* p = (int*)malloc(4);) |
| 失败处理 | 抛异常(默认)或返回 NULL | 返回 NULL |
| 数组处理 | new[]/delete[] | 需手动计算总字节数(malloc(5*4)) |
六、总结
- 核心作用:new 手动分配堆内存(解决栈内存局限),delete 手动释放堆内存(避免泄漏);
- 配对规则:
new配delete,new[]配delete[],缺一不可; - 避坑要点:
- 释放内存后指针置空(
nullptr),避免野指针; - 数组释放必须加
[],自定义类型会影响析构函数调用; - 避免指针重赋值导致内存泄漏;
- 释放内存后指针置空(
- 面试对比:new/delete 是 C++ 运算符,自动调用构造 / 析构;malloc/free 是 C 函数,仅处理内存。
907

被折叠的 条评论
为什么被折叠?



