- A+
内存分配时机
在进入大括号前,会将大括号中需要的内存全部分配好,但是,构造函数只有在对象被实例化的时候才被调用
内存分配顺序
1、分配内存空间
2、调用构造函数
3、调用析构函数
4、回收内存空间
实例化方式
int main() { A a(1); //栈中分配 A b = A(1); //栈中分配 A* c = new A(1); //堆中分配 delete c; return 0; }
第一种和第二种没什么区别,一个隐式调用,一个显式调用,两者都是在进程虚拟地址空间中的栈中分配内存,而第三种使用了new,在堆中分配了内存。栈中内存的分配和释放是由系统管理,而堆中内存的分配和释放必须由程序员手动释放。
动态内存原理
动态分配内存时,空间在堆中分配,但同时,会维护一张表格,该表格中记录了堆中内存的地址和该空间大小。如 int *p = new int;int的大小为4个字节,所以表中中产生一条记录,记录大小为4个字节,并指向堆中内存。 int *a = new int[10],则会开辟40个字节大小的空间,并记录在表格中。调用了new动态开启内存,必须调用delete手动释放内存,delete的方式有两种:delete a和delete [] a。如果new时使用了[ ],则delete的时候也必须加[ ]。下面以Student类为例进行讲解,Student对象的空间大小为16字节。
Student *q = new Student();//开辟了一个16个字节的空间,并在表中记录了大小及地址
Student *r = new Student[10];//开辟了160个字节的空间,并在表中记录了大小及地址
delete q;//在表中寻找和q相等的记录,首先根据q的类型调用相应的析构函数,再去记录中找到相应的大小和地址,回收所有内存
delete r;;//在表中寻找和q相等的记录,首先根据q的类型调用相应的析构函数,再去记录中找到相应的大小和地址,回收所有内存;但是,只会对第一个Student 对象调用析构函数,后面9个Student 对象不会调用析构函数,并且程序会报错。
delete [] r;;//在表中寻找和q相等的记录,首先根据q的类型调用相应的析构函数,再去记录中找到相应的大小和地址,回收所有内存;会对所有的Student 对象都调用析构函数
上图中,有句代码,a++;delete[] a;
由于先调用了a++,导致a的地址和表格中存储的地址不一样,也就导致了delete过程中匹配记录的时候无法找到相应的记录,也就无法回收内存,程序会报错。
delete使用的注意事项
代码测试
ps,大家最好手写代码试验下
三个文件分别如上,如果将main文件中的第20行代码,改成delete [] p; 结果如下,发现后构造的对象最先被delete。
如果改成delete p;报错,结果如下,并报错。