C++:内存管理

C++内存管理的概念

        C语言内存管理方式(malloc/free)在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理

new/delete       

                new/delete操作内置类型:

int main()
{
	int* p1 = (int*)malloc(sizeof(int));

	//动态申请一个int对象
	int* p2 = new int;

	//动态申请一个int对象并初始化为1
	int* p3 = new int(1);

	//动态申请一个长度为4的数组并初始化为0
	int* p4 = new int[4] {0};

	free(p1);
	delete p2;
	delete p3;
	delete [] p4;

	return 0;
}

                以下是关于代码的解释:

                int* p1 = (int*)malloc(sizeof(int)):因为C++是C的延申,所以C++兼容C语言,在C++里也可以使用C语言的代码,malloc从堆中分配了一个int类型的空间,这个大家都熟悉.

                int* p2 = new int;这里使用C++新引入的关键字new,new跟malloc操作类似,都是向申请了一个int类型的空间

                int* p3 = new int(1);使用new向堆申请了一个int类型的空间,但不同的是在类型后面跟了(1),通过调试可以看到在申请完空间的同时将空间内的数据初始化为1。

                int* p4 = new int[4] {0};使用new申请了一个长度为4的int数组,并将数组中的所有元素初始化为0。

                free(p1);释放使用malloc分配的内存。

                delete p2; delete同样也是C++新引入的关键字,用于释放动态分配内存的运算符,后面代码跟这段类似就不展开了。

     

                new/delete操作自定义类型:

class  A
{
public:
	 A(int a=0)
		 :_a(a)
	 {
		 cout << "A():" << this << endl;
	 }

	 ~A()
	 {
		 cout << "~A():" << this << endl;
	 }

private:
	int _a;
};

int main()
{
	A* p1 = (A*)malloc(sizeof(A));
	cout <<"p1:"<< p1 << endl;

	A* p2 = new A(1);
	cout << "p2:" << p2 << endl;
	free(p1);
	delete p2;

	return 0;
}

	

        上图代码创建了两个对象指针,唯一区别的是p1指向的空间是通过malloc分配来的。p2指向的空间是通过new分配来的。

        可以很明显的看出在通过new在堆上创建对象,会调用构造函数初始化对象,以及delete释放空间的时候会调用析构函数。

        

        那么当通过new申请5个对象数组时候,通过上图代码会发现编译器为给每个对象都进行默认构造。如果将默认构造的参数删除变成普通的构造函数,那么再次运行程序编译则会报错。除非给每个对象都传入参数。

        小结:    

         new/delete 和 malloc/free最大区别是 new/delete对于 自定义类型 除了开空间 还会调用构造函数和析构函数。

operator new与operator delete函数:

        new和delete是用户进行动态内存申请和释放的操作符operator new 和operator delete
系统提供的全局函数new在底层调用operator new全局函数来申请空间,delete在底层通过
operator 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);
}

   

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源码:

#define free(p) _free_dbg(p, _NORMAL_BLOCK)

        通过上图operator new源码可以看见在operator new源码里同样调用了malloc,但是operator new会在使用完malloc后进行判断是否申请内存成功,如果不成功会抛出异常(显示异常状态) 。

        operator delete与free同样都执行_free_dbg这段代码,但operator delete会做更多的处理,比如当重复释放同一块空间,operator delete则会抛出异常,让程序员更清楚的知道问题出现在哪。

        上图代码写了一个try  catch用于捕捉异常,以及do while的死循环,p1每次都向堆申请1MB的空间。通过任务管理器可以看到此时vs的运行窗口直接占用了2G左右的内存就不动了,说明此时堆上已经没有空间申请,当程序继续运行也没有发生而是捕捉到了编译器抛出的异常,输出bad allocation(糟糕的分配),提示程序员问题的出处。

       小结:

        通过上述两个全局函数的实现知道,operator new 实际也是通过malloc来申请空间,如果 malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施 就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。   

new和delete的实现原理                 

                实现内置类型:

        如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:
new/delete申请和释放的是单个元素的空间,new[ ]和delete[ ]申请的是连续空间,而且new在申
请空间失败时会抛异常,malloc会返回NULL。
                

                实现自定义类型:

        上图通过new从堆上申请了一个A类的对象空间,通过p1指针进行接受,接着释放p1。下图将通过调试的反汇编可以很清晰明了的观察到new和delete是如何实现创建自定义类型的对象。

        可以看到当使用new创建对象的时候并不是直接申请对象,而是先调用operator new从堆上申请一块空间,接着调用构造函数 A(int a),对对象进行初始化。

        delete在一开始执行的时候会调用一个函数,这个函数封装了A类的析构函数以及operator delete。可以看到delete会先调用析构函数,将对象内的成员进行析构,再调用operator delete释放堆上对象占用的空间。

        

        从上图例子并不能很好的看出new/delete的区别,接下来小编将实现一个栈的数据结构,让读者更好的理解为什么C++中会使用new/delete,而不是继续沿用malloc/free

        上图实现了Stack类的数据结构,Stack类中的成员变量_arr是一个指针,在构造函数中向堆申请一块空间。在主函数里用new向堆中申请一块空间存放一个Stack对象,用st指针进行接收。new会先进行开空间,接着调用构造函数又给成员变量_arr申请了一块空间。当对st进行释放的时候,delete会先调用析构函数,释放_arr所申请的空间,再释放Stack对象的空间。

   

总结 new/delete与malloc/free的区别:

                
        1. malloc和free是函数,new和delete是操作符
        2. malloc申请的空间不会初始化,new可以初始化
        3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,
如果是多个对象,[]中指定对象个数即可。
        4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
        5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需
要捕获异常
        6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new
在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理释放
  • 17
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值