int main() {
int *p = new int[5]; // 分配包含5个整数的数组内存
// 初始化数组元素
for (int i = 0; i < 5; i++) {
p[i] = i * 10;
}
// 试图使用 delete p; 来释放数组内存
delete p;
delete[] p;
// 打印数组元素
for (int i = 0; i < 5; i++) {
std::cout << "p[" << i << "] = " << p[i] << std::endl;
}
return 0;
}
代码如上,在delete p;的时候会发生什么问题,又是什么原因导致的?
首先,需要知道delete在底层都做了什么:
-
调用析构函数:如果
p
指向一个对象(而不是基本数据类型的数组),C++ 运行时将调用这个对象的析构函数。对于基本数据类型(如 int、float 等)或者数组,析构函数不会被调用,因为它们没有析构函数。 -
内存回收:
delete
操作将释放p
指向的内存块。这块内存之后可能被操作系统回收,或者留待将来重新分配。 -
清理内部管理信息:C++ 运行时维护了一些内部数据结构来管理动态分配的内存。
delete
操作将更新这些数据结构,标记相应的内存块为“可用”
而int *p = new int[5];中的p是指向该数组首地址的指针,如果你是用delete p;其实只是做了:
1. 删除了p这个对象
2. 回收p所指向内存(数组首个元素)
3. 清理内部管理信息
所以,数组剩下的元素是没有被清理回收的,我用ide输出了结果,发现delete p的结果如下:
p[0] = 1490466881
p[1] = 5
p[2] = -720962155
p[3] = 305593618
p[4] = 40
都是一些奇怪的值,也能证明刚才提到的delete p所发生的的事情。
如果你分配了数组,要进行释放,使用delete[] p; p = nullptr;如下:
int main() {
int *p = new int[5]; // 分配包含5个整数的数组内存
// 初始化数组元素
for (int i = 0; i < 5; i++) {
p[i] = i * 10;
}
delete[] p;
p = nullptr;
// 打印数组元素
for (int i = 0; i < 5; i++) {
std::cout << "p[" << i << "] = " << p[i] << std::endl;
}
return 0;
}
那delete[] p;底层实现是怎么做的呢?以下:
-
查找数组大小:运行时使用
p
指针回退到数组大小存储的位置,获取数组的大小。 -
调用析构函数:如果数组元素是类对象,并且有析构函数,运行时将为数组中的每个元素调用析构函数。运行时知道数组的大小,所以它可以正确地循环通过每个元素来调用析构函数。
-
内存回收:运行时释放整个数组占用的内存块,包括存储数组大小的那部分内存。
-
清理内部管理信息:运行时更新其内部数据结构,标记相应的内存块为“可用”。
那代码中还有一个问题,我们一般new会调用构造函数,代码如下:
int *p = new int[5];
int *p = new int[5]();
以上两个有什么区别呢?简单来说:
-
int *p = new int[5];
- 不会初始化数组中的元素。拥有未定义的值,取决于内存初始状态。
- 必须在使用数组之前手动初始化每个元素。
-
int *p = new int[5]();
- 会将数组中的每个元素初始化为0。
- “值初始化”(value-initialization)。当你在
new
表达式后面加上一对空括号()
时,触发值初始化。对于基本数据类型如int
,初始化为0。 - 确保数组的每个元素都被初始化为一个确定的值,减少了出错。
大概是这些~,有帮助就点个赞吧!