delete[]调用全部析构函数是因为编译器为new[]分配的数组额外存储元素个数,delete[]读取该信息逐个调用;而delete仅调用首元素析构函数,其余内存直接释放,导致资源泄漏和未定义行为。

对象数组释放时,析构函数调用次数 = 数组长度,前提是使用 delete[] 正确释放;若误用 delete,则仅调用第一个元素的析构函数,其余未定义行为。
为什么 delete[] 才会调用全部析构函数
编译器为 new[] 分配的对象数组额外维护元信息(如元素个数),delete[] 会读取该信息并逐个调用每个元素的析构函数。而 delete 假设操作的是单个对象,只对首地址调用一次析构函数,后续内存直接交还堆管理器——成员资源泄漏、std::string 或 std::vector 内部指针悬空等风险立刻出现。
-
new T[n]→ 必须配对delete[] ptr -
new T→ 必须配对delete ptr - 混用会导致未定义行为,多数编译器不会报错,但运行时崩溃或静默泄漏很常见
std::vector 替代方案更安全
手动管理对象数组极易出错,尤其涉及非平凡析构函数时。std::vector 自动处理构造/析构顺序和内存释放,且支持移动语义,避免深拷贝开销。
std::vector<Resource> vec(10); // 构造 10 个 Resource 对象 // 退出作用域时,自动按逆序调用 10 次 ~Resource()
- 无需显式
new/delete - 析构顺序仍遵循“后构造先析构”,即下标 9 的对象最先析构
- 若需动态增长,
vec.emplace_back(...)保证构造与析构一一对应
继承体系下数组析构的陷阱
基类指针指向派生类对象数组时,仅靠 delete[] 不足以保证正确析构——基类析构函数必须是 virtual,否则派生类部分不会被清理。
立即学习“C++免费学习笔记(深入)”;
Base* arr = new Derived[5]; // 危险!即使 delete[] arr,也只调用 Base::~Base() 5 次 delete[] arr; // ❌ 派生类析构函数未执行
- 正确做法:基类声明
virtual ~Base() = default; - 即便如此,仍不推荐用裸指针管理多态数组;改用
std::vector<std::unique_ptr<Base>> - 注意:
std::unique_ptr<Base[]>支持数组删除,且自动调用delete[]和虚析构
最易被忽略的一点:析构函数调用次数本身不难验证,但错误释放方式导致的“看似正常”(比如程序没崩)最容易让人误判逻辑正确——实际资源早已泄漏,只是还没触发崩溃条件。


















