【C++】深入解析C/C++内存管理:new与delete的使用及原理(二)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 【C++】深入解析C/C++内存管理:new与delete的使用及原理

【C++】深入解析C/C++内存管理:new与delete的使用及原理(一)https://developer.aliyun.com/article/1617320


第一点:

int* p0 = (int*)malloc(sizeof(int));
  int* p1 = new int;

第二点:

int* p2 = new int[10];
  int* p3 = new int(10);
  int* p4 = new int[10]{ 1,2,3 };

第三点

struct ListNode
{
  ListNode* _next;
  int _val;
  ListNode(int val)
    :_val(val)
    ,_next(nullptr)
  {}
};
//创建不带哨兵位,同时如果是插入数据,new ListNode(3)即可
ListNode* CreateList(int n)
{
  ListNode head(-1);
  
  ListNode* tail = &head;
  int val;
  
  printf("请依次输入%d个节点的值:>", n);
  for (size_t i = 0; i < n; i++)
  {
    cin >> val;
    tail->_next = new ListNode(val);
    tail = tail->_next;
  }
  return head._next;
}

第四点:

void func()
{
  int n = 1;
  while (1)
  {
    int* p = new int[1024 * 1024*100];
    cout <<n<<"->"<< p << endl;
    ++n;
  }
}
int main()   
{
   try
  {
    func();
  }
  catch (const exception& e)
  {
    cout << e.what() << endl;
  }
    return 0;
}

这里try和catch就是捕捉异常,这一点到后面有涉及。以上种种都是new的优点,所以我们不推荐再使用malloc/free系列。

四、 new和delete原理及其两个全局函数的实现(operator new/operator delerte)

new和delete是用户进行动态内存申请和释放的操作符operator new和operator delete是系统提供的全局函数,new再底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间(operator new与operator delete不是对new和delete的重载)

int* p1 = (int*)operator new(10 * 4);
  //int* p1=new int(10*4)
  operator delete(p1);
  //delete(p1)

从代码中可以看出来,new/delete和operator new/operator delete效果上是相同的。那么直接使用new/delete就行,operator new/operator delete对于我们来说是没用的,但是有这个东西说明在系统中有它们的一席之地的。

/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
    // try to allocate size bytes
    void *p;
    while ((p = malloc(size)) == 0)
    //通过上述两个全局函数的实现知道,operator new 实际也是通过malloc来申请空间,如果
    //malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施
    //就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。
    if (_callnewh(size) == 0)
    {
    // report no memory
    // 如果申请内存失败了,这里会抛出bad_alloc 类型异常
    static const std::bad_alloc nomem;
    _RAISE(nomem);
    }
    return (p);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
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实际是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施;如果用户提供该措施就继续申请,否则就抛异常。operator delete最终是通过free来释放空间的。

内置类型:

如果申请的是内置类型的空间,new/malloc与delete/free基本类似,不同的地方是new在申请空间失败时会抛异常,malloc会返回NULL

自定义类型:

new的原理:

  1. 调用operator new函数申请空间
  2. 在申请的空间上执行构造函数,完成对象的构造

delete的原理:

  1. 在空间上执行析构函数,完成对象中资源的清理工作
  2. 调用operator delete含函数释放对象的空间

new T[N]的原理:

  1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
  2. 在申请的空间上执行N次构造函数

delete[]的原理:

  1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
  2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

通过汇编,深入立即其中

对于自定义类型转换指令只有两个核心动作调用全局函数及其构造或析构,而内置类型只有调用全局函数。

对此可得:

  • operator new是对malloc的封装,如果失败抛异常,实现new
  • operator newp[]封装operator new,最终还是malloc
  • operator delete对free的封装
  • operator delete[]封装operator delete

同时这里需要注意调用顺序上的问题


【C++】深入解析C/C++内存管理:new与delete的使用及原理(三)https://developer.aliyun.com/article/1617323

相关文章
|
25天前
|
存储 Java 编译器
Java内存模型(JMM)深度解析####
本文深入探讨了Java内存模型(JMM)的工作原理,旨在帮助开发者理解多线程环境下并发编程的挑战与解决方案。通过剖析JVM如何管理线程间的数据可见性、原子性和有序性问题,本文将揭示synchronized关键字背后的机制,并介绍volatile关键字和final关键字在保证变量同步与不可变性方面的作用。同时,文章还将讨论现代Java并发工具类如java.util.concurrent包中的核心组件,以及它们如何简化高效并发程序的设计。无论你是初学者还是有经验的开发者,本文都将为你提供宝贵的见解,助你在Java并发编程领域更进一步。 ####
|
27天前
|
自然语言处理 编译器 Linux
|
28天前
|
算法 Java 数据库连接
Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性
本文详细介绍了Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性。连接池通过复用数据库连接,显著提升了应用的性能和稳定性。文章还展示了使用HikariCP连接池的示例代码,帮助读者更好地理解和应用这一技术。
40 1
|
8天前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
37 0
|
14天前
|
API 持续交付 网络架构
深入解析微服务架构:原理、优势与实践
深入解析微服务架构:原理、优势与实践
17 0
|
15天前
|
存储 供应链 物联网
深入解析区块链技术的核心原理与应用前景
深入解析区块链技术的核心原理与应用前景
|
15天前
|
存储 供应链 安全
深度解析区块链技术的核心原理与应用前景
深度解析区块链技术的核心原理与应用前景
24 0
|
4月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
392 0
|
2月前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
64 1
|
2月前
|
存储
共用体在内存中如何存储数据
共用体(Union)在内存中为所有成员分配同一段内存空间,大小等于最大成员所需的空间。这意味着所有成员共享同一块内存,但同一时间只能存储其中一个成员的数据,无法同时保存多个成员的值。