C++ 中的 new 和 delete
new 和 delete 是 C++ 中用于动态内存管理的操作符,它们在堆 上分配和释放内存。

(图片来源网络,侵删)
new 操作符
new 的作用是在堆上分配一块内存,并返回一个指向该内存起始地址的指针。
语法:
// 分配单个对象 指针类型 指针变量 = new 数据类型; // 分配单个对象并初始化 指针类型 指针变量 = new 数据类型(初始值); // 分配一个数组 指针类型 指针变量 = new 数据类型[元素数量];
示例:
#include <iostream>
int main() {
// 1. 分配一个 int 类型的对象
int* p1 = new int;
*p1 = 10;
std::cout << "p1 points to: " << *p1 << std::endl;
// 2. 分配一个 int 对象并直接初始化为 20
int* p2 = new int(20);
std::cout << "p2 points to: " << *p2 << std::endl;
// 3. 分配一个包含 5 个元素的 double 数组
double* p3 = new double[5];
p3[0] = 1.1;
p3[1] = 2.2;
std::cout << "p3[0] is: " << p3[0] << std::endl;
// ... 后面需要记得 delete ...
delete p1;
delete p2;
delete[] p3; // 注意数组用 delete[]
return 0;
}
delete 操作符
delete 的作用是释放由 new 分配的内存,防止内存泄漏。

(图片来源网络,侵删)
语法:
// 释放单个对象 delete 指针变量; // 释放数组 delete[] 指针变量;
重要规则:
delete必须与new成对出现:用new分配的内存,最终一定要用delete释放,否则会导致内存泄漏,即这块内存一直被占用,直到程序结束。- 匹配使用:
- 用
new分配的单个对象,必须用delete释放。 - 用
new[]分配的数组,必须用delete[]释放。 - 绝对不能混用!
delete[] p1;或delete p3;是错误的,会导致未定义行为,可能引发程序崩溃或数据损坏。
- 用
- 不要重复释放:对同一块内存释放两次也是未定义行为。
- 空指针安全:对
nullptr调用delete是安全的,但不会做任何事。
new 和 delete 的工作原理(以 new int 为例)
当你写下 int* p = new int; 时,C++ 编译器实际上在背后做了两件事:
- 调用
operator new:这是一个内存分配函数,它的唯一任务是在堆上找到一块足够大的、连续的、未被使用的内存空间,并返回这块内存的起始地址,如果分配失败,它会抛出std::bad_alloc异常(而不是返回NULL,这是 C 语言malloc的做法)。 - 调用构造函数:如果分配成功,编译器会调用该数据类型的默认构造函数(对于
int等基本类型,这个步骤通常是空操作,但对于自定义类,会调用其构造函数)。
同样,当你写下 delete p; 时,编译器也做了两件事:

(图片来源网络,侵删)
- 调用析构函数:
p指向的是一个对象(自定义类的实例),编译器会先调用该对象的析构函数,执行一些清理工作(如释放成员指针指向的内存)。 - 调用
operator delete:这是一个内存释放函数,它的任务是释放operator new分配的那块内存,将其归还给操作系统,以便后续再次分配。
这个“分配内存 + 调用构造函数”和“调用析构函数 + 释放内存”的两步过程是 C++ new/delete 相比 C malloc/free 最核心的优势,它使得 C++ 能够完美地管理对象的生命周期。
C++ new/delete vs C malloc/free
这是一个非常重要的对比,能帮助你理解 C++ 内存管理的设计思想。
| 特性 | C++ new / delete |
C malloc / free |
|---|---|---|
| 语言 | C++ | C |
| 本质 | 操作符 | 库函数 |
| 功能 | 分配内存 调用构造函数 |
仅分配内存 |
| 调用析构函数 释放内存 |
仅释放内存 | |
| 返回值 | 成功返回指向类型化内存的指针 | 成功返回 void* (无类型指针),需要手动强制转换 |
| 失败处理 | 抛出 std::bad_alloc 异常 |
返回 NULL 指针 |
| 数组处理 | new[] / delete[] 是语法的一部分 |
需要手动计算数组大小 malloc(n * sizeof(int)) |
| 与类型的关系 | 与类型紧密绑定,知道要构造什么类型 | 与类型无关,只关心字节数 |
示例对比:
假设我们有一个简单的 C++ 类:
class MyClass {
public:
MyClass() { std::cout << "Constructor called" << std::endl; }
~MyClass() { std::cout << "Destructor called" << std::endl; }
};
使用 C++ new/delete:
MyClass* obj = new MyClass; // 输出: Constructor called // ... 使用 obj ... delete obj; // 输出: Destructor called
可以看到,构造和析构函数被正确调用了。
使用 C malloc/free (在 C++ 中也可以用,但不推荐):
MyClass* obj = (MyClass*)malloc(sizeof(MyClass)); // 不会调用构造函数 // ... 使用 obj (obj 的状态是未初始化的) ... free(obj); // 不会调用析构函数
可以看到,malloc/free 只负责内存的分配和释放,完全忽略了对象的构造和析构。MyClass 的构造函数中做了重要的初始化工作,那么使用 malloc 得到的对象就是“有毒”的,处于无效状态。
现代 C++ 的替代方案:智能指针
手动管理 new 和 delete 非常容易出错,是 C++ 中最常见的 bug 来源之一(如忘记 delete、重复 delete、指针悬垂等)。
现代 C++ (C++11 及以后) 强烈推荐使用智能指针来管理动态内存,它们可以自动释放内存,从根本上避免了内存泄漏。
std::unique_ptr
- 特点:独占所有权,一个
unique_ptr在任何时刻都只能指向一个对象,当unique_ptr离开其作用域时(例如函数结束),它所指向的内存会被自动delete掉。 - 优点:轻量级,没有额外的性能开销,非常安全。
- 使用场景:当你需要一个对象,并且这个对象的生命周期应该由其作用域决定时。
#include <iostream>
#include <memory> // 需要包含头文件
void use_unique_ptr() {
std::cout << "Entering use_unique_ptr" << std::endl;
// 创建一个 unique_ptr,它自动管理 new 出来的 MyClass 对象
std::unique_ptr<MyClass> ptr(new MyClass); // 输出: Constructor called
// 使用 ptr...
// ptr.get() 可以获取原始指针,但不推荐转移所有权
// 当函数结束时,ptr 离开作用域,其析构函数会被自动调用
// 析构函数内部会自动执行 delete ptr;
// 输出: Destructor called
std::cout << "Exiting use_unique_ptr" << std::endl;
} // <-- 自动释放内存发生在这里
int main() {
use_unique_ptr();
return 0;
}
std::shared_ptr
- 特点:共享所有权,多个
shared_ptr可以指向同一个对象,它会维护一个引用计数,当最后一个指向该对象的shared_ptr被销毁时,内存才会被delete。 - 优点:适用于需要共享资源所有权的情况,比如在多个数据结构中保存同一个对象的引用。
- 使用场景:当多个所有者都需要访问同一个对象时。
#include <iostream>
#include <memory>
#include <vector>
void use_shared_ptr() {
std::cout << "Entering use_shared_ptr" << std::endl;
std::shared_ptr<MyClass> p1(new MyClass); // 引用计数 = 1, 输出: Constructor called
{
std::shared_ptr<MyClass> p2 = p1; // p2 共享所有权,引用计数 = 2
std::cout << "Inside inner scope, ref count: " << p2.use_count() << std::endl;
} // p2 离开作用域,引用计数减 1,变为 1,内存不释放。
std::cout << "Exiting inner scope, ref count: " << p1.use_count() << std::endl;
} // p1 离开作用域,引用计数减 1,变为 0,内存被自动 delete,输出: Destructor called
| 特性 | new / delete |
malloc / free |
智能指针 (unique_ptr, shared_ptr) |
|---|---|---|---|
| 语言 | C++ | C | C++ (现代) |
| 安全性 | 低,极易出错 | 低 | 高,自动管理内存 |
| 易用性 | 低,需手动成对使用 | 低 | 高,RAII 机制 |
| 核心功能 | 内存 + 构造/析构 | 仅内存 | 内存 + 构造/析构 + 自动释放 |
| 推荐度 | 仅在需要底层控制时使用 | 在 C++ 中不推荐 | 强烈推荐,现代 C++ 的最佳实践 |
核心建议:
- 在 C++ 中,优先使用智能指针 (
std::unique_ptr或std::shared_ptr),而不是原始的new和delete,这是最安全、最现代的内存管理方式。 - 如果必须使用原始指针,请务必确保
new和delete成对出现,并且正确使用delete和delete[]。 malloc和free是 C 语言的遗产,在 C++ 代码中应尽量避免使用,除非你正在与 C 代码库交互或进行一些底层的、与对象生命周期无关的内存操作。
