在 C 语言中,我们一般使用 malloc 与 free 来申请与释放动态内存;而在 C++ 中,一般建议使用 new 与 delete 来申请与释放堆内存。
从功能上来讲,new 与 delete 只是多了一个类的自动构造与析构的过程,其他功能基本相同。
一个简单的使用 new 与 delete 的程序源码如下。
int main() {
int *p = new int(1);
delete p;
return 0;
}
编译成汇编(gcc -S)。
call __main
movl $4, %ecx
call _Znwm
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
movl $1, (%rax)
movq -8(%rbp), %rax
testq %rax, %rax
movl $4, %edx
movq %rax, %rcx
call _ZdlPvm
可见 new 与 delete 调用了两个函数:_Znwm 与 _ZdlPvm。
他们也可称作 new 与 delete 操作符函数。
一、new 与 delete 操作符函数
new 与 delete 操作的操作符函数,简化下来如下。
void* operator new(size_t size) {
return malloc(size);
}
void* operator new[](size_t size) {
return malloc(size);
}
void operator delete(void *p) {
free(p);
}
void operator delete[](void *p) {
free(p);
}
在真正的实现中,情况会稍微复杂一点点。
void* _CRTDECL operator new(size_t size) _THROW1(_STD bad alloc) {
void *p;
while ((p = malloc(size)) == 0 ) {
if (_callnewh(size) == 0) {
// 无内存,抛出 bad_alloc 类型异常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
}
return p;
}
void operator delete(void *pUserData) {
_CrtMemBlockHeader *pHead;
RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
if (pUserData == NULL) return;
_mlock(_HEAP_LOCK); // 对堆内存操作进行线程安全保护
__TRY
pHead = pHdr(pUserData);
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)); // 验证 block 类型
_free_dbg(pUserData, pHead->nBlockUse);
__FINALLY
_munlock(_HEAP_LOCK);
__END_TRY_FINALLY
return;
}
new 与 delete 操作符无法重载,但他们的操作符函数是可以重载的。
重载的格式如下。
// 全局操作符函数重载
void* operator new(size_t size [, param1, param2, ...]);
void operator delete(void *p, [, param1, param2, ...]);
// 类内部操作符重载
class CA {
public:
void* operator new(size_t size [, param1, param2, ...]);
void operator delete(void *p [, param1, param2, ...]);
};
二、new 与 delete 操作符的真正实现
对于基本数据类型,new 操作符直接申请对应大小的堆内存,并返回地址给对应指针;
而对于复杂的类对象,new 操作符会先申请对应大小的堆内存,然后调用对象的默认构造函数,最后返回地址给对应指针。
delete 操作符直接 free 对应地址即可。
对于数组的 new 与 delete,他们会多一道工序。
编译器会多申请一点内存来存储数组的长度,并且存储数组长度的内存置于最前面。
然后根据数组的长度对每个对象进行 new 与 delete。
new 一个数组,如 int *p = new int[3]。
编译器会帮忙申请一个 unsigned long 的内存,内存分布如下。
unsigned long (size)
int (p[0])
int (p[1])
int (p[2])
delete 对应数组时,会先根据 size 将所有申请的数组 free,然后将 size 的内存释放。
for (int i = 0; i < size; ++i)
free(&p[i]);
free((unsigned int *)p - 1);
为什么要一个一个 free?
因为对于对象来说,在释放内存前,每个对象还需要析构。
C++内存管理
本文深入解析C++中new与delete操作符的实现细节,包括它们如何申请与释放内存,以及在类对象和数组中的特殊处理。
1605

被折叠的 条评论
为什么被折叠?



