C++之内存管理

C++之内存管理

C中的内存管理

malloc

calloc

realloc

上述三种方式是用户在堆上开辟空间的办法,用完后必须用free释放,否则会造成内存泄露。

值得一提的是实际开辟的内存比申请的空间大:前面多申请32个字节,后面多申请4个字节。具体原因这里就不再阐述,详情见C语言动态分配内存

C++中的内存管理

C语言内存管理方式在C++中可以继续使用,同时C++又提出了自己的内
存管理方式:C++中通过new和delete运算符进行动态内存管理。

new和delete、new[]和delete[]一定匹配使用

1. new 申请内存

new的用法:

new 数据类型 ;                  //申请内存空间。
new 数据类型   (初值);          //申请内存空间时,并指定该数据类型的初值。
new 数据类型   [内存单元个数];    //申请多个内存空间。

new的样例:

int *p1 = new int;  //申请一个大小为int的空间给p1
int *p2 = new int(2); //申请一个大小为int的空间给p2,并且初始化为2
int *p3 = new int[3]; //开辟一个大小为3个整型的连续空间给p3

源码剖析:
在调试过程中查看源码:

// new int
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
                static const std::bad_alloc nomem;
                _RAISE(nomem);
                }

        return (p);
        }

// new int(3)
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
                static const std::bad_alloc nomem;
                _RAISE(nomem);
                }

        return (p);
        }


// new int[3]
void *__CRTDECL operator new[](size_t count) _THROW1(std::bad_alloc)
    {   // try to allocate count bytes for an array
    return (operator new(count));
    }

可以看到,无论是哪种方式使用new来申请空间,实际上都是调用malloc函数。如果调用malloc函数申请空间失败,就会调用_callnewh(size)在堆上做出处理,然后继续尝试malloc。如果做处理时失败,就会抛出异常。

因此,使用new来开辟空间时,也就不需要用户判断是否成功开辟了空间。如果调用new返回地址了,那么这段地址的空间一定是能用的;如果开辟空间失败,则会直接报错。

对于类:
new做的事:
1.调用operator new分配空间
2.调用构造函数初始化空间
new[N]做的事:
1.调用operator new分配空间
2.调用N次构造函数分别初始化每个对象

2. delete 释放内存

detele的用法:

detete 变量名; //释放单个数据类型内存
delete [] 变量名; //释放数组空间

detele的使用样例:

delete p1;  //释放p1
delete p2;  //释放p2
delete[] p3;    //释放连续数组空间

源码剖析:
C中的free:

extern "C" _CRTIMP void __cdecl free(
        void * pUserData
        )
{
        _free_dbg(pUserData, _NORMAL_BLOCK);
}

C++中delete:

// delete p1
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;
}

// delete p2
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;
}

//delete[] p3
void operator delete[]( void * p )
{
    RTCCALLBACK(_RTC_Free_hook, (p, 0))

    operator delete(p);
}

同样,可以看到delete实际上就是对free函数做了封装。delete p;只是释放*p中的内容,而不改变指针p本身,p存放的依然是申请空间时的地址。

对于类:
delete做的事:
1.调用析构函数清理对象
2.调用operator delete释放空间
delete []做的事:
1.调用N次析构函数清理对象
2.调用operator delete释放空间

3. 定位new表达式

定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象,此不再从堆中申请新的内存。

//例子:
char * buffer = new char [500];
TestData *data1 = new (buffer) TestData; //在new上已有内存上构建对象
TestData *data2 = new (data1 + 1) TestData; //再次利用new在data1
                                            //之后构建第二个TestData对象

定位new不同与常规的new运算符,定位new运算符不需要相应的delete运算符来释放内存。因为它本身就不开辟新的内存。
简单来说就是new运算符只是返回传递给它的地址,并将其强制转换为void *,以便能够赋给任何指针类型。

用将定位new运算符来创建新的类对象后,当该对象消亡时,程序并不会自动地调用其析构函数,所以必须显示地调用析构函数。这个少数的需要显示调用析构函数的情况之一。

对于使用定位new运算符创建的对象,应以与创建顺序相反的顺序进行删除。原因在于,晚创建的对象可能依赖于早创建的对象。另外,仅当所有对象都被销毁后,才能释放用于储存这些对象的缓冲区。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值