【C++系列】内存管理-new与delete的使用以及实现原理


  在C语言中,我们想要动态分配内存空间需要使用到malloc,calloc,realloc函数,malloc函数申请空间失败返回指针空值,成功返回空间首地址。
  在C++中我们同样有动态进行内存管理的方式,并且与C语言中的内存管理有着一些区别。

new/delete
  在C++中我们使用new进行内存的申请,用delete进行内存的释放。他们的使用比malloc和free更加简单方便。

内置类型的内存分配与释放

#include <iostream>
using namespace std;
int main()
{
  int* a = new int;
  //等同于int* a = (int*)malloc(sizeof(int));
  int* b = new int[10];
  //等同于int* b = (int*)malloc(sizeof(int) * 10);
  int* c = new int(10);
  //new还可以进行内置类型的初始化
  cout << *c << endl;
  delete a;
  //等同于free(a);
  delete[] b;//对于多个变量的空间释放要用delete[]
  //等同于free(b);
  delete c;
  //等同于free(c);                                
}

  new和malloc一样会在堆上开辟空间同时需要我们手动进行内存的释放,但是new的写法更加简单易于理解同时我们还可以对单个申请的变量进行初始化。

自定义类型的内存分配与释放

#include <iostream>                                 
#include <stdlib.h>  
using namespace std;                           
class Stu                
{                                                   
  public:                
    Stu()                          
    {                  
      cout << "default building" << endl;
    };              
    Stu(int num, string name):_num(num), _name(name)
    {               
      cout << "custom building" << endl;
    }               
    ~Stu()                  
    {                                
      cout << "destroying" << endl;
    }                         
  private:                  
    int _num;                            
    string _name;        
};                    
int main()
{         
  cout << "malloc:" << endl;
  Stu* a = (Stu*)malloc(sizeof(Stu));
  cout << "new:" << endl;
  Stu* b = new Stu(1, "张三");
  cout << "malloc:" << endl;
  Stu* c = (Stu*)malloc(sizeof(Stu) * 5);
  cout << "new:" << endl;
  Stu* d = new Stu[5];
  cout << "free:" << endl;
  free(a);
  cout << "delete:" << endl;
  delete b;
  cout << "free:" << endl;
  free(c);
  cout << "delete:" << endl;
  delete[] d;
}              


[root@localhost 内存管理]$ ./New 
malloc:
new:
custom building
malloc:
new:
default building
default building
default building
default building
default building
free:
delete:
destroying
free:
delete:
destroying
destroying
destroying
destroying
destroying

  以上这段代码我分别使用malloc和new对自定义类型进行内存分配和释放,我们可以发现new不但可以在分配内存的时候手动调用指定的构造函数还会在分配多个对象的空间时自动调用默认构造函数,delete也会自动调用析构函数,而malloc和free却做不到这一点。 因此可以理解为malloc和free分配出来的只不过是一个和类一样大小的空间,并不能称作是一个对象,而new和delete分配出来的才能被成为对象。

new和delete实现原理

  new和delete在C++中其实被定义为两个运算符,我们在使用这两个运算符的时候它会在底层调用全局函数operator new和operator delete。

operator new/operator delete
  我们首先看下这两个函数在底层的实现源码。

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

  从源码中能看出的是operator newoperator delete在底层也是利用malloc和free分配内存的,因此可以说new和delete不过是malloc和free的一层封装。

operator new
  1.内部通过malloc分配空间
  2.如果malloc申请空间失败,设置空间不足的应对措施,则执行,执行之后继续调用malloc再次申请,直到申请成功。
  如果没有设置应对措施,直接抛异常(内存空间不足)。

operator delete
   封装free,不会抛异常。

  因此在某些情况下,我们想要用独特的方式给一个类分配内存空间的时候我们就可以重新重载这两个运算符来达到我们的目的。基于这个原理如果有某些类需要特殊的内存分配方式我们可以对其进行运算符的重载。

实现原理

内置类型
  对于内置类型来说new和malloc,delete和free的功能一致,不同的是new[]和delete[]才能分配多个连续的空间。

自定义类型

单个元素空间分配
  1、new:

1)首先调用operator new为对象分配空间。

2)调用对象的构造函数对对象进行初始化。

2、delete:

1)调用对象的析构函数进行对象中资源的空间清理。

2)调用operator delete释放对象的空间。

多个元素空间分配
1、new[]:

1)调用operator new[],在operator new[]中调用operator new完成N个对象的空间的分配。

2)调用构造函数N次完成N个对象的初始化。

2、delete[]:

1)调用析构函数N次完成N个对象资源的清理。

2)调用operator delete[],在operator delete[]中调用operator delete完成N个对象的空间的释放

定位new表达式

当我们用malloc或者其他方式分配了一块和某个类一样大小的空间给,并用某个指针去指向这块空间。但是问题在于这块空间并未执行构造函数因此并不能称为对象。因此定位new表达式就是为了帮助我们对一块已经分配好的空间执行构造函数使之成为对象的一个方式。

用法

new (place_address) type(initializer-list)。

place_address为指向某一块空间的指针,type为自定义类型,initializer-list为参数列表。

#include <iostream>                                 
#include <stdlib.h>
using namespace std;
class Stu                                          
{                   
  public:    
    Stu()           
    {                         
      cout << "default building" << endl;
    };                     
    Stu(int num, string name):_num(num), _name(name)
    {                         
      cout << "custom building" << endl;   
    }                      
    ~Stu()              
    {                       
      cout << "destroying" << endl;
    }                         
  private:   
    int _num;               
    string _name;
};                            
int main()     
{                                    
  Stu* p = (Stu*)malloc(sizeof(Stu));
  cout << "定位new表达式:" << endl;
  new(p) Stu(1,"张三");              
  delete p;              
}                                    


[root@localhost 内存管理]$ ./New
定位new表达式:
custom building
destroying

这样我们就用定位new表达式给已经分配好的空间调用了构造函数。

常见问题

new和malloc的异同

相同
  1、new和delete都在堆上进行空间的申请。

2、都需要手动释放空间。

不同
  1、malloc和free是函数而new和delete是运算符。

2、new可以在分配空间的时候进行初始化。

3、malloc返回值是void*需要强转,new会直接返回与分配空间类型一样的类型指针。

4、malloc需要手动计算分配空间大小在进行传入,而new只需要类型和元素个数,空间大小会自动计算。

5、new在给自定义类型分配空间的时候会自动调用其构造函数,delete会自动调用其析构函数。

6、malloc申请空间失败会返回NULL,new会抛异常。

7、new和delete是malloc和free的一层封装,因此效率会低一些。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值