1.malloc/free和new/delete的区别
malloc/free和new/delete都是用于内存申请和释放的,但是具体区别知多少?
1.1 new/delete是关键字,malloc/free是库函数(需要include头文件才能用),malloc/free更多的是和operator new/operator delete类似
1.2 malloc申请内存需要显示填入大小,new不用,举例:
//malloc/free库函数使用范例
int* ptr = (int) malloc(4); //malloc返回void* 类型指针,需要强转
free(ptr);
//new/delete关键字使用范例
int* ptr = new int;
delete ptr;
1.3 返回类型不一样,malloc返回的是void*需要进行强转,new返回的是对应类型的指针,无需强转(类型安全的)
1.4 内存分配失败的返回不同,malloc返回NULL,new不会返回,只会抛出异常,如果不捕获异常,会导致程序崩溃
1.5 内存扩容机制不一样,malloc分配的内存空间如果不足了,可以用realloc来扩容,扩容过程是realloc先检查当前内存空间后面是否还有足够的连续空间,如果有就后面继续申请,并返回原来的指针,如果后面没有足够的,就会另外在别的地方申请一片内存空间,并把原来的内容拷贝到新的内存中,返回新的地址指针,new是动态分配内存,不存在扩容操作
2.new和delete背后的机制
在c++中,new和delete随处可见,但是其背后的机制有了解吗?
//示例
class A
{
public:
A(int v) : var(v){
fopen_s(&file, "test", "r");
}
~A(){
fclose(file);
}
private:
int var;
FILE *file;
};
A* pA = new A(5);
delete pA;
如上new一个A的对象,经过了三个步骤
首先,调用operator new(即new操作符,可以被重载来在栈上分配内存)来申请一个堆内存
其次,调用placement new来在申请好的内存上调用构造函数来初始化对象
最后,返回对象指针
delete一个对象指针,经过了两个步骤
首先,调用placement delete来调用析构函数
最后,调用operator delete来释放内存空间
关于new和delete的过程,如下:
new过程:
来源:https://blog.csdn.net/u010732356/article/details/53958293
delete过程:
来源:https://blog.csdn.net/u010732356/article/details/53958293
了解了new/delete背后的机制后,那么你对new[]和delete[]知多少?
new[]和delete[] 是用于分配数组和释放数组的,它们最好成对出现,为什么说最好,而不是一定要?下面来分析
基本数据类型和自定义类型(类、结构体等)的数组在调用delete和delete[]上有些差别,具体体现在:
1.基本数据类型,可以用delete和delelte[]都是正确的
int *ptr = new int[10]; //内存已经确定
delete ptr; //方式1
delete[] ptr; //方式2
方式1和方式2都正确,按照前面new/delete背后机制的理解,new10个int,由于没有构造函数和析构函数,在new的时候不会构造10次也不会析构10次,ptr指向的内存块最后释放掉,因此不会有任何问题
2.自定义的数据类型,此时delete和delete[]使用需与new和new[]成对出现
A* pAa = new A[3];
delete pAa; //方式1
delete[] pAa; //方式2
根据c++基础知识,方式1会有问题,而方式2是正确的,那么为什么呢?
首先我们需要搞明白,new[]这个过程发生了什么,实际上c++在调用new[]生成对象数组时候,会分配一个大小为4个字节的空间来保存数组的长度(比如上例长度是3),在delete[]时候取出数组长度,这样才能知道数组中有多少能被删掉了
具体过程如下:
首先,调用operator new[]来申请内存,注意要多4个字节用于保存数组长度(基本数据类型没有析构,因此没必要多分配4个字节,下面讨论的问题都是基于自定义类型)
其次,调用placement new[]来在已申请的内存上调用对象构造函数初始化对象
最后,返回指向数组首地址的指针,注意指针指向的位置不是从额外分配的4字节开始的
来源:https://blog.csdn.net/u010732356/article/details/53958293
那么delete[]的过程就很明了了
首先,取出内存空间中的前4个字节得到数组长度,依次对数组中对象调用placement delete[](在这里调用析构函数),因为个数是知道的,那么会准确无误的析构掉数组中每一个对象
最后,operator delete[](pAa),根据pAa指向的地址 - 4个字节地址得到要释放的空间的首地址,然后全部释放
来源:https://blog.csdn.net/u010732356/article/details/53958293
搞清楚new[]和delete[]原理后,那么new/delete和new[]/delete[]要成对使用的问题就很简单了
delete pAa;
上面delete做了两件事
首先,调用placement delete 执行pA指向的对象(数组第一个对象)的析构函数,因此会导致其他两个对象没有调用析构,如果对象上持有资源(如文件、端口等)就会造成无法释放等问题,后果严重
其次,调用operator delete来是释放掉pAa指向的内存空间+首地址4个字节
delete[] pAa; //会依次调用析构函数
上面delete[]做了两件事
首先,调用placement delete[] 执行pAa指向的数组中的所有对象析构函数
最后,调用operator delete[]来是释放掉首地址为pAa地址 - 4个字节的地址所有空间
那么new一个对象用delete[] 来释放会有问题吗?
A* ptr = new A;
delete[] ptr;
根据上面的理解,来分析这个过程
new过程:operator new申请一块内存(注意这里不会额外多出4个字节),placement new初始化对象,返回对象的地址指针
delete[]过程:placement delete[]会取当前指针指向地址之前的4个字节空间中的内容来作为数组长度依次调用析构,调用operator delete[] 释放指针指向地址 - 4字节为首地址的内存空间,那么指针地址 - 4个字节的内存空间中存的数据是未知的,可能会造成严重的问题