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.mzph.cn/news/771032.shtml

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

相关文章

Windows如何搭建 ElasticSearch 集群

单机 & 集群 单台 Elasticsearch 服务器提供服务&#xff0c;往往都有最大的负载能力&#xff0c;超过这个阈值&#xff0c;服务器 性能就会大大降低甚至不可用&#xff0c;所以生产环境中&#xff0c;一般都是运行在指定服务器集群中。 除了负载能力&#xff0c;单点服务器…

map china not exists. the geojson of the map must be provided.

map china not exists. the geojson of the map must be provided. 场景&#xff1a;引入echarts地图报错map china not exists. the geojson of the map must be provided. 原因&#xff1a; echarts版本过高&#xff0c;ECharts 之前提供下载的矢量地图数据来自第三方&…

[LeetCode]LCR 081. 组合总和

题目 思路 先找出数组中最小元素&#xff0c;与目标数比较&#xff1a; 若目标数小&#xff0c;则无组合可能&#xff1b; 若相等&#xff0c;则输出该最小元素&#xff1b; 若目标数大&#xff0c;则寻找一元素的组合可能&#xff0c;寻找二元素的组合可能 以candidates [2,3…

Future机制实际应用

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 常见的两种创建线程…

vue-office/docx插件实现docx文件预览

1.下包 //预览docx文件 npm install vue-office/docx vue-demi//如果是vue2.6版本或以下还需要额外安装 vue/composition-api2.引入 <template><div>//在src填入文档地址<VueOfficeDocx srchttp://...../xx.docx style"width:80%" rendered"re…

C++ 3.25作业

1、定义自己的命名空间&#xff0c;其中有string类型的变量&#xff0c;再定义两个函数&#xff0c;一个函数完成字符串的输入&#xff0c;一个函数完成求字符串长度&#xff0c;再定义一个全局函数完成对该字符串的反转 #include <iostream>using namespace std;namesp…

如何从外网访问内网服务器?

在网络通信中&#xff0c;内网服务器指的是位于私有网络内部的服务器&#xff0c;它们可以提供各种服务&#xff0c;如网站、应用程序等。由于安全性的考虑&#xff0c;内网服务器通常无法直接从外部网络访问。本文将介绍如何通过使用【天联】组网来实现从外网访问内网服务器的…

基于Spring Boot+Vue的美食推荐商城系统

末尾获取源码作者介绍&#xff1a;大家好&#xff0c;我是墨韵&#xff0c;本人4年开发经验&#xff0c;专注定制项目开发 更多项目&#xff1a;CSDN主页YAML墨韵 学如逆水行舟&#xff0c;不进则退。学习如赶路&#xff0c;不能慢一步。 目录 一、项目简介 二、开发技术与环…

Element-Plus下拉菜单边框去除教程

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

2016年认证杯SPSSPRO杯数学建模C题(第二阶段)如何有效的抑制校园霸凌事件的发生全过程文档及程序

2016年认证杯SPSSPRO杯数学建模 C题 如何有效的抑制校园霸凌事件的发生 原题再现&#xff1a; 近年来&#xff0c;我国发生的多起校园霸凌事件在媒体的报道下引发了许多国人的关注。霸凌事件对学生身体和精神上的影响是极为严重而长远的&#xff0c;因此对于这些情况我们应该…

【Unity】调整Player Settings的Resolution设置无效

【背景】 Build时修改了Player Settings下的Resolution设置&#xff0c;但是再次Building时仍然不生效。 【分析】 明显是沿用了之前的分辨率设定&#xff0c;所以盲猜解决办法是Build相关的缓存文件&#xff0c;或者修改打包名称。 【解决】 实测修改版本号无效&#xf…

Windows服务器性能监控

Windows服务器操作系统设计用于运行在客户端-服务器架构内的服务器上&#xff0c;这些服务器通常设计用于处理繁重的工作负载&#xff0c;并作为企业中涉及的大多数软件操作的骨干。因此&#xff0c;为了防止由于性能问题而导致的任何服务损失并保持操作的无缝流&#xff0c;Wi…

Linux-进程控制(进程创建、进程终止、进程等待)

一、进程创建 1.1 fork函数介绍 在命令行下我们可以通过 ./ exe文件 来创建一个进程&#xff0c;通过fork函数&#xff0c;我们可以通过代码的形式从一个进程中创建一个进程&#xff0c;新进程为子进程&#xff0c;原进程为父进程&#xff0c;子进程在创建时&#xff0c;会与…

教育建筑智慧能源管理平台解决方案【新型电力系统下的绿色校园能源管理平台】

一、行业特点 1.建筑类型多&#xff1a;集教学、科研、生活于一体&#xff0c;占地面积大&#xff0c;建筑类型多&#xff0c;功能划分复杂。 2.供电可靠性要求高&#xff1a;教育建筑中的高层建筑、图书馆、实验楼等特级和一级负荷比较多&#xff0c;一旦发生故障会危及生命…

STM32 ESP8266模块的曲折探索

这是本文的配套资料&#xff0c;最终工程请参考 新_ESP8266资料\stm32f103成功移植的项目 【免费】stm32f103c8t6esp8266资料资源-CSDN文库 一、等到了ready 产品参数 我使用的是ai-thinker的esp8266-01s&#xff0c;以下为产品规格书 引脚定义&#xff1a; 依据引脚定义&…

android studio忽略文件

右键文件&#xff0c;然后忽略&#xff0c;就不会出现在commit里面了 然后提交忽略文件即可

如何在VS Code上搭建 C/C++开发环境

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、什么是VScode VScode&#xff08;Visual Studio Code&#xff09;是一款由微软开发的免费开源的轻量级代码编辑器。它…

如何成功将自己开发的APP上架到应用商店

随着移动应用市场的蓬勃发展&#xff0c;开发一款优秀的APP已成为许多企业和个人的首要选择。然而&#xff0c;成功上架并有效推广APP至关重要。本文将逐步介绍完整的上架流程&#xff0c;包括准备所需材料、注册开发者账户、进行APP备案、提交审核以及上架成功后的推广和维护。…

电子商务营销中大数据分析|电商大数据采集API接口的应用

随着经济的不断发展&#xff0c;网络信息技术不断加强&#xff0c;电子商务和大数据的蓬勃发展极大地方便了人们的生活。本文章主要阐述大数据分析与电商营销的含义、大数据分析在电子商务营销中的应用&#xff0c;以及该应用的作用和存在哪些不足及解决方法。探究大数据分析在…

【MATLAB源码-第14期】基于matlab的2ASK的误码率BER仿真以及原信号调制信号解调信号波形展示。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 幅度偏移调制&#xff0c;又称幅移键控&#xff0c;幅度键移&#xff08;英语&#xff1a;Amplitude-shift keying&#xff0c;ASK&#xff09;是通过载波的幅度变化来表示数字信号的一种幅度调制方式。在一个ASK系统中&…