内存区域:c/c++内存管理_第1张图片
由这张图我们可以看到程序在内存中的分布,不同的变量存放在不同的区域,而内核空间是用户无法进行读写的用来存储有关的信息。
要注意栈向下生长,堆向上生长,堆栈相对而生,我们今天主要谈在堆上申请空间的相关知识,我们知道在c语言中想要在堆上申请空间需要用到malloc、realloc、calloc这类函数,关于这些函数的相同与不同点大家可以参考我的另一篇博客。

在c++中我们用心的关键字 new、delete完成对空间的申请和释放。语法是也是定义一个指针变量接受开辟的地址,int p=new int;c++允许我们对申请的空间初始化,只要在后面跟上(),()内为要初始化的内容即可。同时也可以用int p=new int[];的方式申请连续的内存空间,这种方式在释放时的方式为delete [] p;。

注意:在申请自定义类型时new和malloc是类似的,那么意味着对于内置类型free和delete是可以混合使用的,用new申请的空间用free释放也不会出现什么错误,而对于用户自定义类型new和delete会分别调用构造函数和析构函数,而malloc和free则不会,malloc申请的是与对象大小相同的一块内存空间,是不会构成对象的,所以我们在申请和释放空间时要注意配对!

我们在这里谈谈malloc的原理:我们知道malloc申请大小为10个字节的空间时系统会为我们申请大于十个字节的空间,大概申请来的空间是长这个样子滴:
c/c++内存管理_第2张图片
这也就是我们在free的时候系统会知道free多少个字节,因为在这个类似于结构体的结构内最前面已经保存了我们申请空间大小的数值了。

operator new与operator delete函数

这两个函数是new和delete在底层调用的函数,也就是这两个函数为我们完成了内存的开辟和释放。
我们首先看看operator new这个函数:

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 memory
            // 如果申请内存失败了,这里会抛出bad_alloc 类型异常
            static const std::bad_alloc nomem;
            _RAISE(nomem);
        }

    return (p);
}

从这个函数我们可以看到底层也是通过malloc实现的,那么malloc申请成功则直接返回,malloc申请失败尝试执行空间不足的应对措施,执行这个措施就会继续申请,否则抛出异常。所以new函数一般都不会申请失败。

我们再来看operator delete这个函数:

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_FINALLY

        return;
}

通过这个我们知道底层也是通过free实现的就ok。

我们再看看对于自定义类型new和delete的原理:
new的原理

  1. 调用operator new函数申请空间
  2. 在申请的空间上执行构造函数,完成对象的构造
    delete的原理
  3. 在空间上执行析构函数,完成对象中资源的清理工作
  4. 调用operator delete函数释放对象的空间
    new T[N]的原理
  5. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申
  6. 在申请的空间上执行N次构造函数
    delete[]的原理
  7. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
  8. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

最后的最后我们再细谈一下malloc和new的区别:
1.new是操作符、malloc是函数
2.new可以完成对开辟空间的初始化、malloc则不能
3.malloc在使用时需要计算所开辟内存的大小(字节)并传递,而new只需跟上类型即可
4.malloc的返回值是void,所以要强制类型转化,new后是类型则不需要。
5.malloc申请失败会返回NULL,所以要进行判空,new不需要但需要捕获异常
6.malloc和free不会调用构造和析构函数。