C++动态内存管理:new/delete与malloc/free的对比

在C++中,动态内存管理是一个至关重要的概念。它允许我们在程序运行时根据需要动态地分配和释放内存,为对象创建和销毁提供了灵活性。在C++中,我们通常会用到两对工具:new/delete 和 malloc/free。虽然它们都能够完成类似的任务,但在使用、安全性和灵活性方面存在显著差异。

new/delete

  • 类型安全性: newdelete提供了更好的类型安全性。在使用new创建对象时,会自动调用构造函数进行初始化,而在释放内存时,会自动调用析构函数,确保资源正确释放,避免内存泄漏和资源泄漏的风险。

  • 异常处理: new在分配内存失败时会抛出std::bad_alloc异常,这使得在内存分配失败时更容易处理错误情况,从而增强了程序的健壮性。

  • 自动资源管理: newdelete提供了自动资源管理的功能,使得在对象的生命周期结束时能够正确地释放内存,而不需要手动管理。

使用new和delete

#include <iostream>  class MyClass {  
public:  MyClass() {  std::cout << "  MyClass()" << std::endl;  }  ~MyClass() {  std::cout << "~MyClass()" << std::endl;  }  void show() {  std::cout << "Hello!" << std::endl;  }  
};  int main() {  // 使用new分配内存并创建对象  MyClass* obj = new MyClass();  obj->show(); // 输出: Hello!  // 使用delete释放内存并调用析构函数  delete obj; // 输出: ~MyClass() //数组的用法int* pa=new int[6];delete[] pa;  return 0;  
}

malloc/free

  • 内存分配malloc函数根据请求的大小分配一块连续的内存区域,并返回指向该内存的指针。它不会自动初始化内存区域。
  • 内存释放free函数用于释放之前通过malloc分配的内存。它不会调用析构函数或执行任何清理操作。
  • 错误处理:如果malloc无法分配所需的内存,它会返回NULL

使用malloc和free

#include <iostream>  
#include <stdlib.h> // 为了使用malloc和free  int main() {  // 使用malloc分配内存(但不调用构造函数)  void* memory = malloc(sizeof(MyClass));  if (memory == nullptr) {  std::cerr << "Memory allocation failed." << std::endl;  return 1;  }  // 使用"placement new"在已分配的内存上构造对象  MyClass* obj = new(memory) MyClass();  obj->show(); // 输出:Hello! // 手动调用析构函数来销毁对象(但不释放内存)  obj->~MyClass(); // 输出: ~MyClass() // 使用free释放之前通过malloc分配的内存  free(memory);  return 0;  
}

mallocfree是C语言中的函数,虽然它们可以在C++中使用,但通常不推荐,主要原因包括:

  • 不安全的类型转换: malloc返回void*指针,需要手动进行类型转换,容易引起错误。

  • 无构造和析构函数调用: mallocfree只是简单地分配和释放内存,并不调用对象的构造和析构函数。这可能导致对象状态未初始化或未正确清理,容易引发潜在的bug。

  • 不便的异常处理: malloc在分配内存失败时返回NULL,需要手动检查返回值,这在处理错误时不如C++的异常机制方便。

operator new 和 operator delete 函数

在 C++ 中,动态内存管理是至关重要的,而 operator newoperator delete 这两个全局函数则承担了其中的重要角色。它们与 newdelete 操作符紧密相关,负责在底层进行动态内存的分配和释放。让我们深入了解这两个函数的功能和工作原理。

operator new

operator new 的主要任务是分配指定大小的内存块。当使用 new 操作符为一个对象动态分配内存时,实际上会调用 operator new 函数来执行内存分配。这个函数会返回一个指向新分配内存的指针。如果内存分配失败,它通常会抛出一个 std::bad_alloc 异常,除非特别指定了不同的行为。

在标准的实现中,operator new 通常会调用底层的 malloc 函数来实际分配内存,但开发者也可以重载 operator new 以提供自定义的内存分配行为。

operator delete

operator new 相对应,operator delete 的任务是释放之前通过 operator new 分配的内存。当使用 delete 操作符释放一个对象时,实际上会调用 operator delete 函数来执行内存释放。这个函数通常会调用底层的 free 函数来回收内存。

operator new 类似,开发者也可以重载 operator delete 以提供自定义的内存释放行为。

//调用构造和析构A* p = (A*)::operator new(sizeof(A)); //分配new(p) A(); //构造p->~A();   //析构::operator delete(p); //释放//调用构造和析构A* p1 = new A;delete p1;//不会调用构造和析构A*p2 = (A*)operator new(sizeof(A));operator delete(p2);

operator new 和 operator delete 源码:
在这里插入图片描述
详细代码,以及示例可以到[cplusplus]查看(https://legacy.cplusplus.com/reference/new/operator%20new/?kw=operator%20new)

/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,
尝试执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{//try to allocate size bytesvoid *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: 该函数最终是通过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_FINALLYreturn;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)

operator new 的三种形式

  1. throwing

    void* operator new(std::size_t size) throw (std::bad_alloc);
    
    • 当分配失败时会抛出 std::bad_alloc 异常。
  2. nothrow

    void* operator new(std::size_t size, const std::nothrow_t& nothrow_value) throw();
    
    • 当分配失败时返回 nullptr,不会抛出异常。
  3. placement

    void* operator new(std::size_t size, void* ptr) throw();
    
    • 在指定的 ptr 地址上分配内存,不会抛出异常。
    • 这种形式通常用于在已分配的内存上构造对象,例如在内存池中使用。

placement new 的应用

  • 通过在已分配的内存上构造对象,实现在内存池中的对象管理。
  • 调用形式为 new(p) A();,其中 p 可以是动态分配的内存也可以是栈中的缓冲区。
#include <new>// 示例:在已分配的内存上构造对象
void* mem = operator new(sizeof(A)); // 从内存池中获取内存
A* obj = new(mem) A(); // 在 mem 地址上构造 A 对象

placement new 只是返回传入的指针 ptr,不会进行内存分配,而是通过调用对象的构造函数在指定的地址上创建对象。

重载 operator newoperator delete

在某些应用场景中,开发者可能需要实现自定义的内存分配和释放策略,比如使用内存池、堆栈分配器或共享内存等。这时,可以通过重载 operator newoperator delete 来实现。

重载 operator new

在 C++ 中,operator new 可以被重载,允许开发者自定义内存分配方式。在某些应用场景中,开发者可能需要实现自定义的内存分配和释放策略,比如使用内存池、堆栈分配器或共享内存等。这时,可以通过重载 operator new 和 operator delete 来实现。

class A {
public:A() {std::cout << "A()" << std::endl;}~A() {std::cout << "~A()" << std::endl;}// 重载 operator newvoid* operator new(size_t size) {std::cout << " A() operator new" << std::endl;return malloc(size);}// 重载 operator new 以支持 nothrowvoid* operator new(size_t size, const std::nothrow_t& nothrow_value) {std::cout << "A() operator new nothrow" << std::endl;return malloc(size);}
};int main() {A* p1 = new A;  // 调用 A::operator newdelete p1;A* p2 = new(std::nothrow) A;  // 调用 A::operator new nothrowdelete p2;return 0;
}

运行结果:

A() operator new
A() 
~A() 
A() operator new nothrow
A() 
~A() 

如果类 A 中没有定义对 operator new 的重载,那么 new Anew(std::nothrow) A 都将会使用全局的 operator new(size_t size)

自定义参数的 operator new 重载

operator new 重载可以添加自定义参数,这些参数可以在分配内存时传递。虽然这些参数在标准的 operator new 中没有实际用途,但可以用于调试和检测。

void* operator new(size_t size, int x, int y, int z) {std::cout << "X=" << x << "  Y=" << y << " Z=" << z << std::endl;return malloc(size);
}

这种重载看起来可能没有太大的实际作用,因为标准的 operator new 只需完成内存分配任务。然而,通过对这类重载的巧妙应用,可以在动态内存分配的调试和检测中发挥作用。

placement new

placement new 本身是 operator new 的一个重载,它允许在已分配的内存上构造对象,常用于内存池等场景。其调用形式为 new(ptr) A();,其中 ptr 是已分配内存的指针。

void* operator new(size_t size, void* ptr) {return ptr; // 只是简单返回指针,不进行实际内存分配
}

一般情况下,不建议修改 placement new 的实现,因为它通常与 new(ptr) A(); 配合使用,其职责只需简单返回指针。

malloc/free和new/delete的区别

malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地方是:

  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在释放空间前会调用析构函数完成空间中资源的清理

在C++中,一般推荐使用newdelete进行动态内存管理,因为它们提供了更好的类型安全性,并能自动调用构造函数和析构函数,从而减少了内存泄漏和资源泄漏的风险。

什么是内存泄漏,内存泄漏的危害

  • 内存泄漏是指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

  • 内存泄漏的危害在于长期运行的程序出现内存泄漏会影响很大,例如操作系统、后台服务等。出现内存泄漏会导致系统资源越来越稀缺,进而影响系统的响应速度,最终可能导致系统性能下降甚至卡死的情况发生。内存泄漏问题是软件开发中常见但危害严重的问题之一,因此在开发过程中应该严格注意内存管理,避免内存泄漏的发生。

然而,在需要与C代码交互或需要更底层的内存管理控制时,mallocfree可能会被用到。但在这种情况下,需要格外小心以确保正确地管理内存和对象的生命周期。

需要注意的是,尽管mallocfree可以在C++中使用,但并不推荐在C++中经常使用它们进行动态内存管理。因为这可能会导致一些问题,比如忘记初始化或清理对象的资源,从而导致内存泄漏或其他问题。相反,应该优先考虑使用newdelete来利用C++提供的面向对象特性和自动内存管理功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/286676.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

人工心脏术后两个月,他给父母做了一顿饭丨心脏病专家联合访谈

假如人生即将走到尽头&#xff0c;你最后的愿望会是什么&#xff1f; 不同的人会有不同的答案。 对于陈华&#xff08;化名&#xff09;来说&#xff0c;他的愿望是亲手为父母做顿饭。 罹患心脏病多年&#xff0c;陈华的病情反反复复逐渐发展为终末期心衰。虽然自己与父母家仅…

PMP考试费用涨价了?或将涨至4100元!

现在国内线下笔试的PMP报名费是3900元&#xff0c;但最近听到消息&#xff0c;说国外的PMP报名费用已经确认上调&#xff08;从2024年3月1日开始调整&#xff09;&#xff0c;由原本的555美元上调至575美元&#xff0c;根据h率换算&#xff0c;575美元≈4100元。既然国外的报名…

公司调研 | 空间机械臂GITAI | 日企迁美

最近做的一些公司 / 产品调研没有从技术角度出发&#xff0c;而更关注宏观发展&#xff1a;主营方向、产品介绍、商业化落地情况、融资历程、公司愿景、创始人背景等。部分调研放在知乎上&#xff0c;大部分在飞书私人链接上 最近较关注人形Robot的发展情况&#xff0c;欢迎感兴…

SCI一区 | Matlab实现PSO-TCN-BiGRU-Attention粒子群算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测

SCI一区 | Matlab实现PSO-TCN-BiGRU-Attention粒子群算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测 目录 SCI一区 | Matlab实现PSO-TCN-BiGRU-Attention粒子群算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测预测效果基本介绍模型描述…

Capture One Pro 23中文---颠覆性的图像编辑与色彩配置

Capture One Pro 23是一款功能强大且专业的RAW图像编辑处理软件。它拥有全球领先的色彩管理技术和精细的图像编辑工具&#xff0c;可以对图片进行多种精细调整&#xff0c;包括曝光、色温、对比度、锐度等&#xff0c;以满足用户特定的后期处理需求。此外&#xff0c;Capture O…

DNS 服务 Unbound 部署最佳实践

文章目录 安装unbound-control配置启动服务测试 参考&#xff1a; 官网地址&#xff1a;https://nlnetlabs.nl/projects/unbound/about/ 详细文档&#xff1a;https://unbound.docs.nlnetlabs.nl/en/latest/index.html DNS服务Unbound部署于使用 https://cloud.tencent.com/…

Filter、Listener、AJAX

Filter 概念&#xff1a;Filter 表示过滤器&#xff0c;是JavaWeb三大组件(Servlet、Filter、 Listener)之一。 过滤器可以把对资源的请求拦截下来&#xff0c;从而实现一些特殊的功能。 过滤器一般完成一些通用的操作&#xff0c;比如&#xff1a;权限控制、统一编码处理、敏感…

CentOS使用Docker部署Halo并结合内网穿透实现公网访问本地博客

文章目录 1. Docker部署Halo1.1 检查Docker版本如果未安装Docker可参考已安装Docker步骤&#xff1a;1.2 在Docker中部署Halo 2. Linux安装Cpolar2.1 打开服务器防火墙2.2 安装cpolar内网穿透 3. 配置Halo个人博客公网地址4. 固定Halo公网地址 本文主要介绍如何在CentOS 7系统使…

Nextcloud激活被锁用户

Nextcloud激活用户 如果docker下没有安装sudo 和 vim执行下面命令&#xff0c;安装了则跳过 #进入docker内部 #更新apt-get apt-get update #安装sudo apt-get install sudo #安装vim apt-get install vim 修改下面文件内容&#xff0c;否则执行occ命令可能报错 进入上面查询…

连接数据库(MySQL)的JDBC

目录 JDBC简介快速入门API详解DriverManager&#xff08;驱动管理类&#xff09;注册驱动&#xff1a;获取数据库连接(对象)&#xff1a; Connection&#xff08;数据库连接对象&#xff09;获取执行SQL的对象管理事务 Statement(执行SQL语句)执行DML、DDL语句执行DQL语句 Resu…

轨迹预测后处理之非极大值抑制(NMS)

非极大值抑制是图像处理里面的一种算法&#xff08;比如边缘检测会使用到&#xff09; 轨迹预测这里借鉴了其思想&#xff0c;比如说对于某个场景中的某辆车&#xff0c;我们使用模型预测 64 条轨迹或者更多&#xff0c;以很好地捕获多模态性&#xff0c;同时每条轨迹对应一个…

JavaScript 基础、内置对象、BOM 和 DOM 常用英文单词总结

一提到编程、软件、代码。对于英语不是很熟悉的同学望而却步。其实没有想像中的难么难&#xff0c;反复练习加上自己的思考、总结&#xff0c;会形成肌肉记忆。整理一下&#xff0c;初学者每天30遍。 1、JavaScript 基础语法 break&#xff1a;中断循环或 switch 语句的执行。…

Gif动图怎么快速制作?两招教你在线做

Gif动图作为一种实用的图片格式&#xff0c;因为其体积小&#xff0c;画面丰富&#xff0c;所以在各大聊天软件中非常的受欢迎。小伙伴们是不是很好奇这种gif动态图片是如何制作的吧&#xff01;下面&#xff0c;小编就给大家分享两个快速制作gif动画的小技巧&#xff01;不用下…

Matlab从入门到精通课程

教程介绍 MATLAB是美国MathWorks公司出品的商业数学软件&#xff0c;用于数据分析、无线通信、深度学习、图像处理与计算机视觉、信号处理、量化金融与风险管理、机器人&#xff0c;控制系统等领域。 学习地址 链接&#xff1a;https://pan.baidu.com/s/1PxGarBwQusMzwPVqcE…

内网使用rustdesk进行远程协助

文章目录 前言一、搭建rustdesk中继服务器二、搭建文件下载服务器三、创建引导脚本四、使用 前言 内网没有互联网环境&#xff0c;没法使用互联网上有中继服务器的远程协助工具&#xff0c;如teamviewer、todesk、向日癸等&#xff1b;在内网进行远程维护可以自己搭建中继服务…

TS + Vue3 elementUI 表格列表中如何方便的标识不同类型的内容,颜色区分 enum

TS Vue3 elementUI 表格列表中如何方便的标识不同类型的内容&#xff0c;颜色区分 enum 本文内容为 TypeScript 一、基础知识 在展示列表的时候&#xff0c;列表中的某个数据可能是一个类别&#xff0c;比如&#xff1a; enum EnumOrderStatus{"未受理" 1,"…

python能做什么

python能做什么 Web开发&#xff1a;Python具有许多流行的Web框架&#xff0c;如Django和Flask&#xff0c;使得它成为Web开发的首选语言。它简洁、易于学习、且拥有丰富的生态系统&#xff0c;能够快速构建高性能的Web应用。 数据科学和机器学习&#xff1a;Python在数据科学…

51单片机—直流电机

1.元件介绍 2.驱动电路 3.电机调速 一般会保证一个周期的时间是一样的 应用&#xff1a; 1.LED呼吸灯 #include <REGX52.H>sbit LEDP2^0;void Delay(unsigned int t) {while(t--); } void main() {unsigned char Time,i;while(1){for(Time0;Time<100;Time){for(i0;…

【MATLAB源码-第16期】基于matlab的MSK定是同步仿真,采用gardner算法和锁相环。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 **锁相环&#xff08;PLL&#xff09;** 是一种控制系统&#xff0c;用于将一个参考信号的相位与一个输入信号的相位同步。它在许多领域中都有应用&#xff0c;如通信、无线电、音频、视频和计算机系统。锁相环通常由以下几个…

matlab和stm32的安装环境。能要求与时俱进吗,en.stm32cubeprg-win64_v2-6-0.zip下载太慢了

STM32CubeMX 6.4.0 Download STM32CubeProgrammer 2.6.0 Download 版本都更新到6.10了&#xff0c;matlab还需要6.4&#xff0c;除了st.com其他地方都没有下载的,com.cn也没有。曹 还需要那么多固件安装。matlab要求制定固件位置&#xff0c;然后从cubemx中也指定…