必须使用new[]和delete[]配对,因为new[]分配内存并调用每个对象构造函数,delete[]逆序调用析构函数后再释放内存,确保对象生命周期正确管理,避免内存泄漏和堆损坏。

在C++中处理动态对象数组,核心的注意事项在于如何正确地分配内存并妥善地调用每个对象的构造函数,以及在释放时确保每个对象的析构函数都被调用,最后才是回收内存。这远比C语言中简单的
malloc
free
要正确地分配和释放C++动态对象数组,我们必须始终坚持使用
new[]
delete[]
new[]
delete[]
new[]
delete
std::vector
std::unique_ptr<T[]>
new[]
delete[]
说实话,这个问题是C++内存管理中最基础也最容易出错的地方之一。我个人觉得,理解其背后的机制,才能真正避免“知其然而不知其所以然”的困境。
new[]
立即学习“C++免费学习笔记(深入)”;
delete[]
new[]
现在,我们来看
delete[]
delete[]
new[]
delete[]
如果你错误地使用
delete
new[]
delete
delete
new[]
所以,这不仅仅是语法上的规定,更是C++对象生命周期管理的核心逻辑。
#include <iostream>
#include <string>
class MyResource {
public:
std::string name;
MyResource(const std::string& n = "default") : name(n) {
std::cout << "MyResource " << name << " constructed." << std::endl;
}
~MyResource() {
std::cout << "MyResource " << name << " destructed." << std::endl;
}
};
void demonstrate_correct_usage() {
std::cout << "--- Demonstrating correct usage ---" << std::endl;
MyResource* resources = new MyResource[3]{MyResource("A"), MyResource("B"), MyResource("C")};
// ... 使用资源 ...
delete[] resources; // 确保所有析构函数被调用,然后释放内存
std::cout << "--- Correct usage finished ---" << std::endl << std::endl;
}
void demonstrate_incorrect_usage() {
std::cout << "--- Demonstrating incorrect usage (DO NOT DO THIS) ---" << std::endl;
MyResource* resources = new MyResource[3]{MyResource("X"), MyResource("Y"), MyResource("Z")};
// ... 使用资源 ...
// delete resources; // 错误!只调用一个析构函数,可能导致堆损坏和内存泄漏
// 这里为了演示,我们还是用正确的delete[],但请记住delete是错误的
delete[] resources;
std::cout << "--- Incorrect usage finished ---" << std::endl << std::endl;
}
int main() {
demonstrate_correct_usage();
// demonstrate_incorrect_usage(); // 实际项目中不要运行这种错误代码
return 0;
}运行
demonstrate_incorrect_usage
MyResource X destructed.
Y
Z
在C++中,异常安全是一个非常重要的概念,尤其是在涉及资源管理时。手动管理动态对象数组时,异常安全是一个实实在在的痛点。设想一下,如果你用
new MyObject[size]
例如,
MyObject
new[]
new[]
delete[]
手动处理这种场景异常复杂,通常需要编写冗长的
try-catch
catch
智能指针的解决方案: 现代C++中,解决这类问题的黄金法则是RAII (Resource Acquisition Is Initialization),而智能指针正是RAII的典范。对于动态对象数组,
std::unique_ptr<T[]>
std::unique_ptr<T[]>
std::make_unique<T[]>
std::unique_ptr<T[]>
delete[]
unique_ptr
#include <iostream>
#include <memory> // For std::unique_ptr
#include <stdexcept> // For std::runtime_error
#include <vector> // Also a good alternative
class CriticalResource {
public:
int id_;
CriticalResource(int id) : id_(id) {
std::cout << "CriticalResource " << id_ << " constructed." << std::endl;
if (id_ == 1) {
// 模拟在构造第二个对象时发生异常
// std::cout << "Simulating error during construction of CriticalResource " << id_ << std::endl;
// throw std::runtime_error("Failed to initialize CriticalResource 1");
}
}
~CriticalResource() {
std::cout << "CriticalResource " << id_ << " destructed." << std::endl;
}
};
void manual_array_with_exception_risk() {
std::cout << "--- Manual array with exception risk ---" << std::endl;
CriticalResource* arr = nullptr;
try {
// 如果这里 CriticalResource(1) 抛出异常,CriticalResource(0) 将被泄漏
arr = new CriticalResource[3]{CriticalResource(0), CriticalResource(1), CriticalResource(2)};
// 假设这里有一些后续操作可能抛出异常
// throw std::runtime_error("Some other error after array construction");
} catch (const std::exception& e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
// 如果 arr 已经部分构造,这里的 delete[] arr 可能会有问题,
// 或者说,如果异常发生在 new CriticalResource[3] 内部,
// C++ 运行时会负责清理已构造的元素,但如果异常发生在 *之后*,
// 那么没有智能指针就容易忘记 delete[]。
// 为了演示,这里假设 new 本身是成功的,但后续操作失败。
// 实际情况更复杂,但智能指针能简化。
}
// 即使在 catch 块中处理了,也容易遗漏或出错
// delete[] arr; // 如果 arr 是 nullptr,这是安全的,但如果不是,且没在catch中处理,就泄漏了
std::cout << "--- Manual array finished ---" << std::endl << std::endl;
}
void smart_ptr_for_exception_safety() {
std::cout << "--- Smart pointer for exception safety ---" << std::endl;
try {
// std::make_unique<T[]> 是 C++14 及更高版本推荐的创建方式
// 它会负责调用 new T[size]
auto arr_ptr = std::make_unique<CriticalResource[]>(3); // 调用 CriticalResource 的默认构造函数
// 如果 CriticalResource 的构造函数会抛异常,new T[size] 会确保已构造的元素被正确析构
// 这里的 arr_ptr 确保了无论后续代码是否抛出异常,delete[] 都会被调用。
// 例如:
// throw std::runtime_error("Another error after smart pointer array creation");
} catch (const std::exception& e) {
std::cerr << "Caught exception in smart_ptr_safety: " << e.what() << std::endl;
}
// arr_ptr 在这里离开作用域,自动调用 delete[],无需手动管理
std::cout << "--- Smart pointer finished ---" << std::endl << std::endl;
}
int main() {
// manual_array_with_exception_risk(); // 运行这段代码时,请小心处理异常模拟
smart_ptr_for_exception_safety();
return 0;
}通过
std::unique_ptr<T[]>
动态内存管理,尤其是在使用原始指针时,是C++中错误的高发区。除了前面提到的
new[]
delete[]
1. 越界访问 (Out-of-bounds Access): 这是指你试图访问数组中实际不存在的元素,比如访问
arrayPtr[size]
arrayPtr[-1]
[0, size - 1]
std::vector
at()
std::vector
at()
std::out_of_range
以上就是C++动态对象数组分配和释放注意事项的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号