new-delete 实现原理

在 C 语言中,我们一般使用 mallocfree 来申请与释放动态内存;而在 C++ 中,一般建议使用 newdelete 来申请与释放堆内存。
从功能上来讲,newdelete 只是多了一个类的自动构造与析构的过程,其他功能基本相同。

一个简单的使用 newdelete 的程序源码如下。

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

可见 newdelete 调用了两个函数:_Znwm_ZdlPvm
他们也可称作 newdelete 操作符函数。

一、newdelete 操作符函数

newdelete 操作的操作符函数,简化下来如下。

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;
}

newdelete 操作符无法重载,但他们的操作符函数是可以重载的。
重载的格式如下。

// 全局操作符函数重载
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, ...]);
};

二、newdelete 操作符的真正实现

对于基本数据类型,new 操作符直接申请对应大小的堆内存,并返回地址给对应指针;
而对于复杂的类对象,new 操作符会先申请对应大小的堆内存,然后调用对象的默认构造函数,最后返回地址给对应指针。
delete 操作符直接 free 对应地址即可。

对于数组的 newdelete,他们会多一道工序。
编译器会多申请一点内存来存储数组的长度,并且存储数组长度的内存置于最前面。
然后根据数组的长度对每个对象进行 newdelete

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
因为对于对象来说,在释放内存前,每个对象还需要析构。

你可能感兴趣的:(Cool,C++,c++)