一般情况-堆上申请普通变量空间
申请堆空间时,实验表明 new/malloc 与 delete/free 是可以混用的,即可以通过free()释放掉new出来的一块内存。
int main() {
int *p = new int(123);
delete p;
//free(p);
}
特殊情况-堆上申请对象空间
class Person{
public:
Person(){
cout << "construct call ......" << endl;
}
~Person(){
cout << "destruct call ......" << endl;
}
private:
int m_age;
};
int main() {
Person *ptr = new Person();
if (ptr != nullptr)
{
free(ptr);
}
return 0;
}
单步走看内存分布图:
可以看到,执行完free(ptr) 后,内存确实是被释放了。(补充一下:fd作为开始结束的标志,"cd cd cd cd"代表开辟的内存,四个字节)
但是!对于一个对象而言,new和delete关键字还额外做了 调用构造函数和调用析构函数这两个步骤。
可以看到,程序只调用了构造函数(new关键字产生),但是由于使用的是free(),因此并没有调用类中的析构函数。
一般情况-堆上申请普通数组空间
int main() {
int *ptr = new int[10];
delete[]ptr;
//delete ptr;
return 0;
}
我们知道,用new在堆上申请数组空间,一般delete的时候都需要加上[ ] ,即 delete[ ] 。
但实验表明,如果不涉及到类对象,不加[ ]也同样可以实现空间的释放,加不加[ ]是没有区别的。
特殊情况-堆上申请对象数组空间
int main() {
Person *ptr = new Person[10];
delete ptr;
return 0;
}
当new一个对象数组时,如果没有用delete[ ] ,会发生崩溃报错:
修改成delete [ ] ptr后,程序正常运行,并调用了十次构造函数和十次析构函数:
进一步探索:为什么修改为delete [ ] 就会调用十次析构函数?它是如何知道创建了十个对象就一定会析构十个对象?
查看一下此种情况下的内存分布:
可以看到我们申请的堆对象数组空间(10个),仔细查看改起始地址的前一个地址,按道理这并不属于我们分配的空间,为什么会多一个地址?
0x011E4EC8 ,该地址保存的值显示为16进制,转换为十进制刚好为10(0a 00 00 00)。这个10代表着什么?
不妨修改一下这个地址,0a 00 00 00 修改成 07 00 00 00 后接着单步走:
此时原来的十个地址空间的确是完整的被释放了,但是析构函数只被调用了 7次!正好是自己修改的那个内存地址的值。
到这里可以得出结论: 编译器是如何记录new 创建出来数组对象的个数, 就是简单的在创建空间的前一个地址,记录了创建对象的个数,析构的时候就按照这个地址的值进行析构。
总结
1. 申请一个堆上的对象时,不允许混搭new/delete 必须搭配使用。
2. new [ ] 和 delete [ ] 一定要配套使用,特别是申请对象数组时。
3. vs编译器会在new [ ] 申请对象数组时,在堆开始的前4个字节写入当前数组的长度,用于记录delete [ ]释放时候的析构函数调用。
到此这篇关于C++ new与malloc和delete及free动态内存管理及区别介绍的文章就介绍到这了,更多相关C++动态内存管理内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!