C语言中动态内存管理方式
在讲解C++的内存管理方式之前,我们先简单复习一下C语言的动态内存管理方式。
C++的内存分配方式与C相同,一般都分为六块区域:内核空间(用户代码不能读写),栈,内存映射段,堆,数据段,代码段。
其中要注意的部分为:
栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。
堆用于程序运行时动态内存分配,堆是可以上增长的。
数据段--存储全局数据和静态数据。
4.代码段--可执行的代码/只读常量。
简略介绍完了C/C++中程序内存区域划分的方式,我们再进入主题:C语言的动态内存管理方式。
相信大家在学习C++之前都学习过C语言,知晓C语言的动态内存管理方式主要由四个函数完成:malloc/calloc/realloc/free。
简略介绍的话,malloc是用来分配一块指定大小的内存区域。calloc的用法与malloc相近,与malloc不同的是它在分配完内存区域后还会对其进行初始化。而realloc一般是用来更改之前分配的内存区域的大小,可以是增加或减少。当然,如果一开始扩容的区域不存在,realloc也可以当作malloc来使用。
C++内存管理方式
在复习完了C语言的内存管理方式之后,我们来学习C++的内存管理方式。
C语言内存管理方式在C++中可以继续使用,但有许多地方存在不便。C++有自己的内存管理方式,即通过new和delete操作符进行动态内存管理。
接下来是这两个操作符使用的示范:
下面是普通的申请情况
#include <iostream>
using namespace std;
int main()
{
//动态申请一个int类型的空间
int* p1 = new int;
//动态申请一个int类型的空间并初始化
int* p2 = new int(1);
//动态申请5个int类型的空间
int* p3 = new int[5];
//动态申请5个int类型的空间并初始化(未赋值的地方为0)
int* p4 = new int[5] {1,2,3};
delete p1;
delete p2;
delete[] p3;
delete[] p4;
return 0;
}
还有一些特殊的情况,如构造自定义类型的数组(构造单个自定义类型如上面示例演示一样):
#include <iostream>
using namespace std;
class A
{
public:
A(int n, int m)
:_a1(n)
, _a2(m)
{
}
private:
int _a1;
int _a2;
};
int main()
{
//这里只介绍比较特殊便捷的方法
//普通的构造方法就不过多赘述
//1.使用匿名对象构造
A* p1 = new A[3]{ A(1,1),A(2,2),A(3,3) };
//2.使用隐式类型转换构造
A* p2 = new A[3]{ {1,1},{2,2},{3,3} };
delete[] p1;
delete[] p2;
return 0;
}
注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用
new[]和delete[],需要匹配起来使用!
可能有些人就会好奇:如果不匹配使用会如何呢?
如果进行不匹配使用,有些情况可能不会出错,但有些情况就会出现内存泄漏导致程序崩溃,所以保险起见还是进行匹配使用为好。
operator new 和 operator delete 函数
由上面的介绍我们可知:new和delete是用户进行动态内存申请和释放的操作符。而它们是通过调用operator new和operator delelete来实现的相关操作的。
那么什么是operator new函数 和 operator delete函数呢?
operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
这些概念听起来很"高大上",但实际上,这两个函数的底层是通过malloc和free来实现的:


通过上述两个全局函数的实现我们知道,operator new 实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。而且operator delete 最终是通过free来释放空间的
malloc/free和new/delete的区别
malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。
不同的地方是:
malloc和free是函数,new和delete是操作符
malloc申请的空间不会初始化,new可以初始化
malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可
malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需
要捕获异常
- 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理释放
它们的核心区别为第六点,即自定义类型用new/delete能自动调用构造函数和析构函数。
这六点区别,前四点为用法区别,后两点为本质区别。
定位new
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。用来显式调用构造函数
//显式调用构造函数(要通过定位new实现)
A* p2 = (A*)operator new(sizeof(A));
new(p2)A(1);
//析构函数可以直接调用,不需要通过定位new
简单来说,定位new就是用来给一个已分配空间的变量进行初始化
它的应用场景为:内存池(从堆中划分出一块区域专供使用)
但因为想这块区域申请只能得到内存,而构造函数需要自己调用,所以有了定位new这个概念(这部分仅做了解即可)