目录
- C语言中的动态内存管理
- C++动态内存管理
- 动态管理内置类型
- 动态管理自定义类型
- new和delete的实现原理
- operator new和operator delete函数
- new和delete对内置类型的实现原理
- new和delete对自定义类型的实现原理
- malloc/free和new/delete区别
C语言中的动态内存管理
之前学习了C语言中的动态内存管理:C语言中动态内存管理
我们这里简单复习一下:
C语言中动态内存管理的函数:malloc,calloc,realloc,free
int main()
{int* p1 = (int*)malloc(sizeof(int) * 4);int* p2 = (int*)calloc(4, sizeof(int));int* p3 = (int*)realloc(p1, sizeof(int) * 10);free(p2);free(p3);
}
这里我们可以看出,C语言中开辟动态资源,需要类型强转,需要写入开辟具体的字节数
其实还不是很方便
所以C++内存管理解决了这个问题
C++动态内存管理
C++兼容C,所以malloc,calloc,realloc,free在C++中可以继续使用
C++提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理
动态管理内置类型
- 动态申请一个
int
类型空间:
int* p1 = new int;
这里可以看出C++中开辟一个int类型,就直接在new后面写上int类型就可以,在C中,我们需要写入Int类型的大小
这里没有进行初始化,所以开辟出的空间里的是随机值
- 动态申请一个
int
类型空间,并且初始化为3:
int* p2 = new int(3);
C++中,new的同时是可以初始化的,这是C语言中动态内存开辟中不具备的能力
- 动态申请3个
int
类型空间:
int* p3 = new int[3];
这里没有进行初始化,所以开辟出的空间里的是随机值
- 动态申请5个
int
类型空间,并且初始化:
int* p4 = new int[5] {1, 2, 3, 4, 5};
- 释放空间
delete p1;
delete p2;
delete[] p3;
delete[] p4;
申请和释放单个元素的空间,使用
new
和delete
操作符
申请和释放连续的空间,使用new[]
和delete[]
操作符
new
和delete
配合,malloc
与free
配合使用,不要交叉混合使用
动态管理自定义类型
在申请自定义类型空间时,new
会自动调用构造函数,delete
会自动调用析构函数,而malloc
与free
不会
这也是new/delete和malloc/free最大的区别
现在有如下的类:
class A
{
public:A(int a = 0):_a(a){cout << "A(int a = 0)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};
用malloc和free管理一个自定义对象
int main()
{A* pa1 = (A*)malloc(sizeof(A));free(pa1);return 0;
}
可以看到没有任何输出,也就是不会自动调用构造函数和析构函数
接着用new/delete管理一个自定义对象
int main()
{A* pa1 = (A*)malloc(sizeof(A));free(pa1);A* pa2 = new A;delete pa2;return 0;
}
可以看到,结果有输出内容,也就说明new可以调用构造函数,delete会调用析构函数
因为有默认构造函数,所以A* pa2 = new A;
这里自动按照默认构造函数赋值
如果没有默认构造函数,A* pa2 = new A;
这样的写法会报错的
这时就需要传值
A* pa2 = new A(1);
开辟连续空间的自定义对象:
A* pa3 = new A[3]{ 1,2,3 };
上面这种写法会涉及到隐式类型转换,把int类型转成自定义类型
也可以使用匿名对象来进行初始化:
A* pa3 = new A[3]{ A(1),A(2),A(3) };
对于A* pa3 = new A[3]{ A(1),A(2),A(3) };
这样开辟连续空间时,也会多次调用构造函数
同理delete[] pa3
也会多次调用析构函数
new和delete的实现原理
operator new和operator delete函数
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间
面向对象的语言处理失败,不喜欢用返回值,喜欢用抛出异常
所以malloc申请空间失败返回NULL,而new申请失败是抛出异常
operator new 对malloc进行封装,如果失败抛异常
所以operator new不是直接给我们用的,而是给new用的
同理operator delete是对free函数进行封装,同时如果失败抛出异常,operator delete 不是给我们用的,而是给delete用的
下面是operator new和operator delete函数的源码,的确可以看到对malloc和free的封装,以及检测异常的部分:
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0){// report no memorystatic const std::bad_alloc nomem;_RAISE(nomem);}
return (p);
}//operator delete: 该函数最终是通过free来释放空间的
void operator delete(void *pUserData)
{_CrtMemBlockHeader * pHead;RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));if (pUserData == NULL)return;_mlock(_HEAP_LOCK); /* block other threads */__TRY/* get a pointer to memory block header */pHead = pHdr(pUserData);/* verify block type */_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));_free_dbg( pUserData, pHead->nBlockUse );__FINALLY_munlock(_HEAP_LOCK); /* release other threads */__END_TRY_FINALLYreturn;
}
new和delete对内置类型的实现原理
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
new和delete对自定义类型的实现原理
new的原理:先调用operator new函数申请空间,再在申请的空间上执行构造函数,完成对对象的初始化
delete的原理:先调用析构函数,完成对对象中资源的清理工作,再调用operator delete函数
这里需要注意的是:new是先开空间再构造,而delete是先析构再释放空间,不要搞混
下面我们通过一个Stack类能够更好的理解:
class Stack
{
public:Stack(int capacity = 4, int top = 0):_capacity(capacity), _top(top){_a = new int[capacity];}~Stack(){delete[] _a;}private:int* _a;int _capacity;int _top;
};
在运行Stack* pst = new Stack
语句时,只能先开辟一段空间,然后再调用构造函数malloc出一段空间给_a
接着在运行delete[] pst
时,必须先调用析构函数。这里如果先释放空间,则是先释放在堆区中对象的空间,那么_a
指向的空间没有释放,这里造成内存泄露
所以必须先用析构,将数组空间释放,再释放对象整体
其实除了operator new和 operator delete外,还有operator new[]和operator delete[]函数,这两个是用来开辟和释放连续空间的对象
new T[N]:调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的开辟,然后再执行N次构造函数
delete[]:先调用N次析构函数,完成对象中资源的清理,再调用operator delete[]释放空间,operator delete[]中是调用N次operator delete来释放空间
malloc/free和new/delete区别
- malloc和free是函数,new和delete是操作符
- malloc申请空间不可以初始化,new可以
- malloc返回值类型是void*,接收时需要强转,new不需要
- malloc申请空间时,需要手动计算开辟空间的大小,而new不需要,后面跟上空间类型就可以。多个对象时,在[]中指定对象个数即可,不用手动计算大小
- malloc失败后返回NULL,而new失败后会抛出异常
- 申请自定义类型对象时,malloc和free只会开辟和释放空间,不会调用构造和析构函数,而new/delete,申请空间后会调用构造函数,释放空间前会调用析构函数。