动态分配内存
1. new 和 delete
函数原型:
void *operator new(size_t); //allocate an object
void *operator delete(void *); //free an object
1.1 new和delete的运行机制(单个对象)
以类A为例:
class A
{
public:
A(int v) : var(v){
fopen_s(&file, "test", "r");
}
~A(){
fclose(file);
}
private:
int var;
FILE *file;
};
当我们用new创建一个A类的指针数组时,new返回的是指向内存空间的A类型的指针
class *pA = new A(10);
背后完成的工作是
new的工作有以下几点:
- 由于A类的大小是8 Bytes(参考《深入C++对象模型》),那么new函数就会开辟一个8 Bytes的地址空间,但内存空间没有初始化和类型化
- 对内存空间进行类对象的初始化。调用构造函数,给private基本变量赋值,指针变量指向对应位置
- 返回对象指针
当我们用delete释放掉类对象的时候
delete pA;
背后完成的工作是:
delete的工作有以下几点:
- 调用指针所指向对象的析构函数,对打开的文件进行关闭
- 通过调用delete库函数来释放掉该对象的内存空间,传入的参数是指针pA,也就是对象的地址
1.2 用new type[]和delete []申请和释放数组
1.申请和释放基本数据类型的数组空间
这里会发现,创建数组和创建单个对象的不同,这里new 和 delete变成了:
type *name = new type[ ];
delete [] name;
在上面的例子中,释放string类型数组空间时实际上先为10个string对象分别调用析构函数,再释放掉为10个string对象所分配的所有内存空间;而释放int类型数组空间时,因为int是内置类型不存在析构函数,所以直接释放掉了为10个int类型变量分配的所有空间。
因此,非内置类型数据用new type[] 来动态分配内存时,必须保存数据的维度,以确定在析构时需要调用对象析构函数的次数。C++的做法是在分配数组空间时在前面多分配了4 Bytes 大小的空间,专门保存数组的维度,在delete的时候根据数据维度调用析构函数,最后再释放所有内存空间。
以创建pA类对象数组为例说明 Type *name = new Type[] 和 delete[] name的运行机制
首先为对象数组分配内存空间:
class A *pAa = new A[3];
背后的工作是:
当我们释放对象数组的内存空间时:
delete [] pAa;
背后的工作是:
正如前面所说:依次为数组中每个对象调用析构函数,调用析构函数的总次数是由调用new库函数时开辟的内存空间的前4 Bytes 中保存的数据决定的;调用delete[] name 库函数时,其参数不是指针name的值(也是第一个数组元素的地址),而是这个地址值减去4 Bytes
1.3 内存泄漏
2. malloc 和 free
- 头文件是#include<stdlib.h>,函数声明为:
void* malloc(size_t size);
- 参数size_t size表示动态内存分配空间的大小,以字节为单位。
- malloc()函数的返回值是一个指针,或者说是分配后内存空间的首地址.
- 如果malloc()函数申请空间成功则返回一段内存空间的首地址,失败则返回NULL
- 返回指针类型需要强制类型转换(有安全隐患)
int *p = (int *) malloc(sizeof(int));
- 在使用malloc()函数申请的空间之前,最好用memset()函数把这段内存空间清理一下。malloc智能保证内存空间的大小,无法保证是否有垃圾数据
- malloc和free要配对使用,否则会内存泄漏
案例:
//0.包含头文件<stdlib.h>
//1.定义一个char* 指针变量p,并分配10个字节内存空间(字符数组)
char *p = (char *)malloc(sizeof(char));
//2.将字符数组中的内容全部修改为0
memset(p, 0, 10 * sizeof(char));
//3.复制一段字符串到p指向的数组空间(长度要比所分配的内存小)
strcpy(p, "hello ");
//4.用realloc扩充p指向的内存空间
p = (char*)realloc(p, 20 * sizeof(char));
//4.在原字符串后面再拼接一段字符
strcat(p, "world");
//5.释放空间
free(p);
3.new/delete 和 malloc/free的区别
其他(与上文提到的有重复):
- new、delete是C++中的操作符(关键字),通过特定的语法可以组成表达式。malloc和free是标准库函数。
- malloc free是库函数,不是运算符,不在编译器控制范围之内,不能够自动调用构造函数和析构函数。mallloc只是为变量分配了内存,free只是释放变量的内存。
- new在为对象申请分配内存空间时,可以自动调用构造函数,同时也可以完成对对象的初始化。同理,delete也可以自动调用析构函数。
- new返回的是指定类型的指针,并且可以自动计算所申请内存的大小;malloc需要我们计算申请内存的大小,并且在返回时强行转换为实际类型的指针。
- new从自由存储区分配内存;malloc从堆上分配内存。
- 与malloc、free相比,new、delete不仅能动态分配内存空间,而且会调用构造函数、析构函数对对象进行初始化和销毁。
参考文献:
C/C++——C++中new与malloc的10点区别
C++读书笔记—malloc()函数的注意点及使用示例