C++内存结构从低地址到高地址依次可分为:代码区(只读区) - 全局区(静态区) - 堆区 - 栈区。代码区存放编译后的二进制代码;全局区存放程序执行时的全局变量和静态数据;堆区用于存放程序执行时动态分配的内存段;栈区当前执行函数的信息。
程序执行内存分配的三种方式(编译代码不分配内存,只是一个难搞的翻译过程而已,执行的时候才分配):
- 1)从静态存储区域分配。
- 2)在栈上分配。
- 3)从堆上分配,亦称动态内存分配。下面的内容都在堆上操作动态分配内存。
- 如果完全不知道这些内容可以看这里:https://blog.csdn.net/qq_42570601/article/details/107598826
这篇文章对malloc、free和new、delete解释的特别详细,层次也很深:https://www.cnblogs.com/gaochaooo/archive/2009/09/03/1559764.html,如果对这部分内容不是很熟,建议先看一下下面内容,学会基本的语法,再去看这篇博客会事半功倍。
malloc、free函数
分别用于分配、释放内存,操作的是堆内存
malloc、free 使用步骤:申请内存 -> 确认是否申请成功 -> … -> 释放内存 -> 置空指针
为什么置空呢?我们在删除一个指针之后,编译器只会释放该指针所指向的内存空间,而不会删除这个指针本身,如果不置空,就会出现野指针。所以在删除一个指针之后,一定将该指针设置成空指针。如果不明白可以看这里:https://blog.csdn.net/qq_36570733/article/details/80043321
int* array = (int*)malloc(10 * sizeof(int));//申请内存
assert(array != NULL);//使用断言判断是否申请成功
for (int i = 0; i < 10; i++)
*(array + i) = i;
for (int i = 0; i < 10; i++)
std::cout << *(array + i) << std::endl;
free(array);//释放内存,释放完array并不为null
array = NULL;//置空指针
new、delete运算符
分别用于分配、释放内存,操作的是堆内存
- new / new[]:完成两件事,先底层调用 malloc 分配了内存,然后调用构造函数(创建对象)。
- delete/delete[]:也完成两件事,先调用析构函数(清理资源),然后底层调用 free 释放空间。
- new 在申请内存时会自动计算所需字节数,而 malloc 则需我们自己输入申请内存空间的字节数。
使用的基本步骤同malloc、free一样:
int* pa = new int;
assert(pa != NULL);
*pa = 110;
std::cout << *pa << std::endl;
int* pb = new int(111);
assert(pb != NULL);
std::cout << *pb << std::endl;
int* array = new int[10];
assert(array != NULL);
for (int i = 0; i < 10; i++)
*(array + i) = i;
for (int i = 0; i < 10; i++)
std::cout << *(array + i) << std::endl;
delete pa;
pa = NULL;
delete pb;
pb = NULL;
delete[] array;
array = NULL;
new/delete与malloc/free总结
特征 | new/delete | malloc/free |
---|---|---|
分配内存位置 | 自由存储区 | 堆 |
分配成功返回值 | 类型指针 | void*(需要指针类型强制转换) |
分配失败返回值 | 默认抛出bac_alloc异常 | 返回NULL |
分配内存大小 | 编译器根据类型计算 | 必须显示指定字节数 |
处理数组 | new[] | 计算字节数显示指定 |
已分配内存的扩充 | 无法直观地处理 | 使用realloc函数 |
是否相互调用 | 可以,看具体的operator new/delete实现 | 不可调用new |
分配内存时内存不足 | 用户能指定处理函数或重新指定分配器 | 无法通过用户代码进行处理 |
函数重载 | 允许 | 不允许 |
构造函数与析构函数 | 调用 | 不调用 |
delete this 合法吗?
https://isocpp.org/wiki/faq/freestore-mgmt#delete-this
合法,但:
- 必须保证 this 对象是通过 new(不是 new[]、不是 placement new、不是栈上、不是全局、不是其他对象成员)分配的
- 必须保证调用 delete this 的成员函数是最后一个调用 this 的成员函数
- 必须保证成员函数的 delete this 后面没有调用 this 了
- 必须保证 delete this 后没有人使用了