C语言动态内存管理
C语言使用malloc、calloc、realloc来进行动态内存的管理。
malloc仅仅开辟一块空间,calloc开辟空间的同时并把它初始化为0,而realloc可以进行动态内存的调整。前面博客中已经介绍了这三个函数,在此不再赘述。
浅谈malloc、calloc和realloc
//C动态内存管理
void Test()
{
int *p1 = (int*)malloc(sizeof(int)*4);
free(p1);
int *p2 = (int*)calloc(4,sizeof(int));
int *p3 = (int*)realloc(p2,sizeof(int)*4);
free(p2);
free(p3);
}
需要注意的是,动态开辟的内存一定要手动释放,不然会造成内存泄露的危害。
下面我们主要说C++的动态内存管理。
C++动态内存管理
C++通过new和delete来动态管理内存。
new/delete动态管理对象,new[]/delete[]动态管理对象数组。
就像malloc和free要一起配合使用一样,new/delete,new[]/delete[]也要匹配使用。如果new出来的对象用free去释放,那么该对象因无法执行析构函数导致程序出错,相同的,用delete去释放malloc开辟出来的动态内存也有可能导致程序出错。所以,一定要匹配使用。
void Test1()
{
int *p1 = new int;//动态开辟一块四字节空间,单个数据(1个int)
int *p2 = new int(3);//动态开辟一块四字节空间并把它初始化为
int *p3 = new int[3];//动态开辟3个四字节空间(12字节)
delete p1;
delete p2;
delete[] p3;
}
我们知道C++是兼容C的,那么已经有C库的malloc和free了,为什么C++还要定义new和delete来动态管理内存?
malloc和new都可用来动态的开辟内存,而free和delete是用来释放空间。
C++中不仅有内置类型,而且还有自定义类型。对于内置类型,编译器本来就认识,不需要用户去定义,但是对于非内部类型,光用malloc和free不能满足动态对象的要求。对象在创建的时候要自动的去调用构造函数完成初始化,在释放的时候要自动的调用析构函数完成清理工作。由于malloc和free是库函数而不是运算符,不在编译器控制权限之内,所以不能把初始化和清理工作强加给malloc/free。因此C++需要提供一个能够完成动态内存分配和初始化工作的运算符new,以及一个能够完成清理和释放内存工作的运算符delete。
那么malloc/free和new/delete有什么区别和联系呢?
1)它们都是动态管理内存的入口。
2)malloc/free是C/C++标准库的函数,new/delete是C++操作符。
3)malloc/free只是动态分配内存空间/释放空间,而new/delete除了分配空间还会调用构造函数和析构函数进行初始化和清理(清理成员)
4)malloc/free需要手动的计算类型大小且返回值为void*,new/delete可自己计算,返回对应类型的指针。
5)malloc/free失败返回0,new/delete失败抛异常。
看下面这个例子:
class Array
{
public:
//构造函数
Array(size_t size = 10)
:_size(size)
, _a(0)
{
cout << "Array(size_t size)" << endl;
if (_size > 0)
{
_a = new int[size];
}
}
//析构函数
~Array()
{
cout << "~Array()" << endl;
if (_a)
{
delete[] _a;
_a = 0;
_size = 0;
}
}
private:
size_t _size;
int *_a;
};
void Test()
{
Array* p1 = (Array*)malloc(sizeof(Array));
Array* p2 = new Array;
Array* p3 = new Array(3);
Array* p4 = new Array[3];
free(p1);
delete p2;
delete p3;
delete[] p4;
}
上面用了三次new,创建了5个对象,所以调用了5次构造函数和5次析构函数。
结果如下:
C++动态内存管理的过程如下:
1.开空间,调用operator new,底层是用malloc实现的;
2.调用构造函数完成初始化;
3.调用析构函数完成清理工作;
4.释放空间。
在这期间,new做了两件事:
1.调用operator new开空间
2.调用构造函数初始化对象
delete也做了两件事:
1.调用析构函数清理对象
2.调用operator delete释放空间
注意:开空间的时候没有直接调用malloc,而是先调用了operator new,原因是operator new失败了直接抛异常。
自定义类型调用new[]时,会多开四个字节,用来存对象的个数,是为了告诉delete要调多少次析构函数,而内置类型不会。
过程图如下: