目录
前言
本篇文章会涉及到汇编层面的知识,不过没有汇编基础的也可以看,大概知道什么意思就行了,毕竟是来学C++而不是汇编。
使用到的工具是vs2010,平台Windows,编译器msvc
1、我们可以在什么地方创建对象?
假设我们有如下类:
#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>
class Person
{
public:
int x;
int y;
Person(){ printf("无参构造。\n"); }
Person(int x,int y){ printf("有参构造。\n"); }
~Person(){ printf("析构函数。\n"); }
};
int main()
{
system("pause");
return 0;
}
<1> 全局变量区
Person P; // 在函数的外部定义
<2> 栈
void Max() { Person p; } // 在函数内部定义
栈区也就是之前汇编调用函数时提升堆栈的'堆栈',只有在函数调用时才会有堆栈,函数调用结束后该堆栈虽然在,但已经被使用并且不属于这个函数。所以说栈(堆栈)中的数据只有函数调用时存在,函数调用完成后将不存在。
<3> 堆:new delete
在堆中创建对象:
C语言:Person* p = (Person*)malloc(sizeof(Person));
C++: Person* p = new Person(); // 默认调用无参时,括号可以省略。
释放对象占用的内存:
C语言:free(p);
C++: delete p;
2、malloc与new的区别
malloc是C语言提供的用来申请堆区内存的函数,而new是C++中的函数。二者还是有一些区别的,下面将会从汇编的层面去分析区别所在。
首先是malloc的汇编,如下:
断点、编译、调试、ALT+8查看汇编:
可以看到malloc函数就是调用了一个__imp__malloc函数去进行内存申请的(这里就不跟进这个函数内部了,比较乱)。
然后看new的汇编,如下:
查看反汇编:
可以看到new申请内存的话是比malloc严格一些的,主要有三个步骤:
申请、检查、调用构造;
new调用有参构造和无参构造的方式
new调用无参构造的话可以这样:
也可以这样:
new调用有参构造的话传入参数就行了:
小结
大概可以总结一下new与malloc了。
malloc:调用__imp__malloc函数
new:调用operator new函数、检查是否申请成功、调用类的构造
如果跟进__imp__malloc与operator new的反汇编的话,其实跟到最后这二者都是调用一个叫做'HeapAlloc'的函数,这是Windows API函数。(可以之间点击链接查看百度百科解释)
所以:
如果是给类的对象申请堆区空间的话,new = malloc+检查+构造函数
在C语言中也有检查堆区是否申请成功的函数
叫做assert,需要头文件#include <assert.h>
如下:
3、free与delete的区别
直接看汇编:
free的汇编代码如下:
没什么特殊的。
delete:
可以看到delete是先判断指针是否为空,然后调用析构代理函数,与C语言相比还是比较严格的。
什么是析构代理函数?
单步步入F11跟进函数内部,如下:
析构代理函数是这样的:
调用真正的析构、调用delete删除堆区申请的内存。
小结
free: 调用__imp__free函数
delete:检查内存是否已经释放、调用析构函数、调用operator delete函数
所以delete = 检查+析构+free
4、delete与delete[]的区别
我们知道,如果是给类的一个对象申请堆区内存的话,是这样的:
Person* p = new Person;
如果是给一个类的对象数组申请堆区空间的话,是这样的:
Person* p = new Person[10]; // 申请一个Person p[10]大小的堆区空间
如下:
既然是申请数组空间,那么就要用delete[]去释放这个申请的空间。
我们运行一下看看控制台输出的什么?
可以看到申请10个Person对象的堆区空间会调用10次构造函数
释放的时候使用delete[] 也会调用十次析构函数
那么如果new[]申请,delete释放呢?
如下:
会触发中断,可以看到是调用了一次析构函数的,这代表释放了一个Person对象的堆区空间
一次delete会调用一次析构、释放一次内存
而delete[]则是调用多次析构、释放整个数组的堆区空间
那么释放可以循环十次使用delete呢?
不可以
因为第一次进入delete的时候就已经触发了中断、程序是不会继续运行下去的。
小结
delete一次调用一次析构、释放一次堆区空间
而delete[]则是调用多次析构、释放整个数组的堆区空间
new delete 与 new[] delete[]建议配套使用。
感兴趣的话也可以跟进delete与delete[]的汇编,看是如何实现的,这里我就不跟大家操作了
总结
1、创建对象可以在全局区、栈中,也可以new申请堆区空间
2、malloc:调用__imp__malloc函数
new: 调用operator new函数、检查是否申请成功、调用类的构造
__imp__malloc与operator new作用大致一样
3、free: 调用__imp__free函数
delete: 检查内存是否已经释放、调用析构函数、调用operator delete函数
4、delete: 调用一次析构、释放一次堆区空间
delete[]:调用多次析构、释放整个数组的堆区空间
结语
如果有讲的不好的地方可以私信评论之后会修改;讲错了的地方请指出,谢谢大家!