C++新经典 | C++ 查漏补缺(内存)

目录

 一、new和delete

1.new类对象时,括号问题

2.new做了什么事

3.delete做了什么事

4.new与malloc的区别

5.delete与free的区别

二、分配及释放内存

三、重载operator new和operator delete操作符

1.重载类中的operator new和operator delete操作符

(1)为数组分配内存

2.重载全局operator new和operator delete操作符

四、内存池

1.内存池要解决的主要问题是什么?

2.内存池的实现原理是什么呢?

五、定位new


一、new和delete

        一般来讲,写C++程序,多数情况下还是提倡使用new和delete,不提倡使用malloc和free(这是C编程风格中才使用的)。

1.new类对象时,括号问题

(1)如果是一个空类,加不加括号没什么区别。

(2)类中如果有成员变量,带括号这种初始化对象的方式会把一些和成员变量有关的内存内容设置为0(内存中显示的内容是0)。

(3)如果类中有构造函数,main中的这两行代码执行的结果又变得相同了,如果构造函数中没有给age初始化,那么最终age的值没有被初始化为0,而是一个随机的值。

class TestMemory {
public:int age;
};void main()
{TestMemory *p = new TestMemory();TestMemory *p1 = new TestMemory;std::cout << p->age << std::endl;      //0std::cout << p1->age << std::endl;     //-842150451
}

2.new做了什么事

        new关键字主要做了两件事:①一个是调用operator new;②一个是调用类的构造函数。(调试中可以使用F11键(或选择“调试”→“逐语句”命令)跳转进operator new,发现operator new调用了malloc)new关键字的调用关系如下表示:

    TestMemory *p = new TestMemory();operator newmallocTestMemory::TestMemory();

3.delete做了什么事

        delete关键字释放内存时的大概调用关系(注意调用顺序)如下:

    delete p;TestMemory::~TestMemory();//如果有析构函数,先调用析构函数operator delete();free(); 

4.new与malloc的区别

(1)new是关键字/操作符,而malloc是函数。

(2)new一个对象的时候,不但分配内存,而且还会调用类的构造函数(当然如果类没有构造函数,系统也没有给类生成构造函数,那没法调用构造函数了)。

(3)另外刚才也看到了,在某些情况下,“A *pa=newA();”可以把对象的某些成员变量(如m_i)设置为0,这是new的能力之一,malloc没这个能力。

(4)new最终是通过调用malloc来分配内存的。

5.delete与free的区别

        delete不但释放内存,而且在释放内存之前会调用类的析构函数(当然必须要类的析构函数存在)。

二、分配及释放内存

        分配内存这件事,假设分配出去的是10字节,但这绝不意味着只是简单分配出去10字节(而是比10字节多很多),而是在这10字节周围的内存中记录了很多其他内容,如记录分配出去的字节数等。(分配内存时为了记录和管理分配出去的内存,额外多分配了不少内存,造成了浪费,尤其是对于频繁分配小块的内存,浪费就显得更加严重)。

        释放一块内存,影响的范围很广,虽然分配内存的时候分配出去的是10字节,但释放内存的时候影响的远远不止是10字节的内存单元,而是一大片。(free一个内存块并不是一件很简单的事,free内部有很多的处理,包括合并临近空闲内存块、登记空闲块的大小、设置空闲块首位的一些标记以方便下次分配等一系列工作。)

    char* point = new char[10];

        总之,编译器要有效地管理内存的分配和回收,肯定在分配一块内存之外额外要多分配出许多空间保存更多的信息。编译器最终是把它分出去的这一大块内存中间某个位置的指针返回给point,作为程序员能够使用的内存的起始地址。也就是说,程序员拿到的point的地址实际上是malloc所分配出去的地址中中间的某个地址。

三、重载operator new和operator delete操作符

1.重载类中的operator new和operator delete操作符

        如果不想用自己写的operator new和operator delete成员函数了,怎样做到呢?当然不需要把类中的operator new和operator delete注释掉,只需要在使用new和delete关键字时在其之前增加“::”(两个冒号)即可。两个冒号叫作“作用域运算符”,在new和delete关键字之前增加“::”的写法,表示调用全局的new和delete关键字。此时,就不会调用类中的operator new和operator delete了。

class TestOverrideOperator {
public:static void* operator new(size_t size) //重载时第一个参数必须时size_t类型{std::cout << "调用重载operator new" << std::endl;TestOverrideOperator* mem = (TestOverrideOperator*)std::malloc(size);return mem;}static void operator delete(void* p)  // 重载时第一个参数必须时void*类型{std::cout << "调用重载operator delete" << std::endl;free(p);}static void* operator new[](size_t size) //重载时第一个参数必须时size_t类型{std::cout << "调用重载operator new[]" << std::endl;TestOverrideOperator* mem = (TestOverrideOperator*)std::malloc(size);return mem;}static void operator delete[](void* p)  // 重载时第一个参数必须时void*类型{std::cout << "调用重载operator delete[]" << std::endl;free(p);}TestOverrideOperator(){std::cout << "调用构造函数" << std::endl;}~TestOverrideOperator(){std::cout << "调用析构函数" << std::endl;}
};
void TestNewAndDelete()
{std::cout << "-------调用重载operator new和重载operator delete------" << std::endl;TestOverrideOperator *test = new TestOverrideOperator();delete test;std::cout << "-------调用全局operator new和全局operator delete------" << std::endl;TestOverrideOperator *test1 = ::new TestOverrideOperator();::delete test1;std::cout << "-------数组测试:调用重载operator new[]和重载operator delete[] ------" << std::endl;TestOverrideOperator* testArray = new TestOverrideOperator[3]();delete[] testArray;std::cout << "------- 数组测试:调用全局operator new[]和全局operator delete[] ------" << std::endl;TestOverrideOperator* testArray1 = ::new TestOverrideOperator[3]();::delete[] testArray1;
}

         因为new和delete本身称为关键字或者操作符,所以类中的operator new和operator delete叫作重载operator new和operator delete操作符,但是这里将重载后的operator new和operator delete称为成员函数也没问题。

(1)为数组分配内存

        从上面的代码可以看出:operator new[]和operator delete[]只会被调用1次,但是类的构造函数和析构函数会被分别调用3次,这一点千万别搞错,不要误以为3个元素大小的数组new的时候就会分配3次内存,而delete也会执行3次。

        将断点设置在operator new[]函数体内,调试起来,观察形参size的值,发现是7。为什么会是7呢?因为这里创建的是3个对象的数组,每个对象占1字节,3个对象正好占用3字节(如下图,地址为0x016EEB44)。另外4字节是做什么用的呢?其实是记录数组大小的,数组大小为3,所以,这4字节(一个int或者unsigned int类型数据的大小)里面记录的内容就是3(如下图,地址为0x016EEB40),可以想象,释放数组内存的时候必然会用到这个数字(3),通过这个数字才知道new和delete时数组的大小是多少,从而知道调用多少次类的构造函数和析构函数。

2.重载全局operator new和operator delete操作符

        也可以重载全局的operator new、operator delete以及operator new[]、operator delete[],当然,在重载这些全局函数的时候,一定要放在全局空间里,不要放在自定义的命名空间里。

        虽然可以重载全局的operator new、operator delete、operator new[]、operator delete[],但很少有人这样做,因为这种重载影响面太广。读者知道有这样一回事就行了。一般都是重载某个类中的operator new、operator delete,这样影响面比较小(只限制在某个类内),也更实用。

        类中的重载会覆盖掉全局的重载。

四、内存池

        使用malloc这种分配方式来分配内存会产生比较大的内存浪费,尤其是频繁分配小块内存时,浪费更加明显。所以一个叫作“内存池”的词汇就应运而生。

1.内存池要解决的主要问题是什么?

  • 减少malloc调用次数,这意味着减少对内存的浪费。
  • 减少对malloc的调用次数后,能不能提高程序的一些运行效率或者说是运行速度呢?从某种程度上来说,能,但是效率提升并不太多,因为malloc的执行速度其实是极快的。

        减少内存浪费是根本,提高程序运行效率是顺带(不是最主要的)的。

2.内存池的实现原理是什么呢?

        就是用malloc申请一大块内存,分配内存的时候,就从这一大块内存中一点点分配给程序员,当一大块内存差不多用完的时候,再申请一大块内存,然后再一点一点地分配给程序员使用。

五、定位new

        除了传统new之外,还有一种new叫作“定位new”,翻译成英文就是placement new,因为它的用法比较独特,所以并没有对应的placement delete的说法。

        定位new的功能是:在已经分配的原始内存中初始化一个对象。

  • 已经分配,意味着定位new并不分配内存,也就是使用定位new之前内存必须先分配好。
  • 初始化一个对象,也就是初始化这个对象的内存,可以理解成其实就是调用对象的构造函数。

        总而言之,定位new就是能够在一个预先分配好的内存地址中构造一个对象。 定位new的格式如下:

new(分配好的内存首地址) 类类型(参数)
void  TestNew()
{student *s = new student();int *p= new(s) int(10);    //new(分配好的内存首地址) 类类型(参数)std::cout << *p << std::endl;
}

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

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

相关文章

vue+element实现电商商城礼品代发网,商品、订单管理

一、项目效果图 1.首页 2.登录 版本2&#xff1a; 3.注册 4.找回密码 5.立即下单 6.商品详情 7.个人中心-工作台 8.个人中心-订单列表 9.订单中心-包裹列表 10.个人中心-工单管理 11.我的钱包 12.实名认证 13.升级vip 14.个人中心-推广赚钱 二、关键源码 1.路由配置 impor…

【机器学习】PyTorch-MNIST-手写字识别

文章目录 前言完成效果一、下载数据集手动下载代码下载MNIST数据集&#xff1a; 二、 展示图片三、DataLoader数据加载器四、搭建神经网络五、 训练和测试第一次运行&#xff1a; 六、优化模型第二次优化后运行&#xff1a; 七、完整代码八、手写板实现输入识别功能 前言 注意…

vue重修【005】自定义路由、插槽

文章目录 版权声明自定义指令指令初识指令中配置项指令语法指令值v-loading指令的封装分析实现 插槽默认插槽插槽默认值具名插槽作用域插槽使用步骤完整案例 版权声明 本博客的内容基于我个人学习黑马程序员课程的学习笔记整理而成。我特此声明&#xff0c;所有版权属于黑马程…

如何快速定位BUG?BUG定位技巧及测试人员定位的N板斧

很多测试人员可能会说&#xff0c;我的职责就是找到bug&#xff0c;至于找原因并修复&#xff0c;那是开发的事情&#xff0c;关我什么事&#xff1f; 好&#xff0c;我的回答是&#xff0c;如果您只想做一个测试人员最基本最本分的事情&#xff0c;那么可以这么想。但是&#…

微信批量发朋友圈,多个号同步

近年来&#xff0c;随着数字营销的飞速发展&#xff0c;越来越多的企业开始将客户引至微信&#xff0c;并通过群发广告和发布朋友圈等方式进行产品推广&#xff0c;以实现高效率、低成本和良好的转化效果。随着号多起来了&#xff0c;朋友圈推广工作变得愈发繁琐&#xff0c;需…

Vue3 + Nodejs 实战 ,文件上传项目--实现图片上传

目录 技术栈 1. 项目搭建前期工作(不算太详细) 前端 后端 2.配置基本的路由和静态页面 3.完成图片上传的页面&#xff08;imageUp&#xff09; 静态页面搭建 上传图片的接口 js逻辑 4.编写上传图片的接口 5.测试效果 结语 博客主页&#xff1a;専心_前端,javascript,mys…

TiDB 7.4 发版:正式兼容 MySQL 8.0

MySQL 是全球最受欢迎的开源数据库&#xff0c;长期位于 DB-Engines Ranking 排行榜第二名&#xff0c;在世界范围内拥有数量庞大的企业用户和开发者。然而&#xff0c;随着时间的推移&#xff0c;MySQL 用户正面临新挑战。Oracle 官宣将在 2023 年 10 月终止 MySQL 5.7 版本的…

2023,简历石沉大海?软件测试岗位真的已经饱和了....

各大互联网公司的接连裁员&#xff0c;政策限制的行业接连消失&#xff0c;让今年的求职雪上加霜&#xff0c;想躺平却没有资本&#xff0c;还有人说软件测试岗位饱和了&#xff0c;对此很多求职者深信不疑&#xff0c;因为投出去的简历回复的越来越少了。 另一面企业招人真的…

【halcon】halcon轮廓总结之select_contours_xld

前言 select_contours_xld 我认为是一个非常常用且实用的算子&#xff0c;用于对轮廓进行筛选。 简介 这段文档描述了一个名为"SelectContoursXld"的操作&#xff0c;用于根据不同特征选择XLD&#xff08;XLD是一种图像数据表示形式&#xff0c;表示轮廓线&#x…

竞赛 深度学习+python+opencv实现动物识别 - 图像识别

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 inception_v3网络5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; *…

Golang学习:基础知识篇(二)—— 数组及切片

Golang学习&#xff1a;基础知识篇&#xff08;二&#xff09;—— 数组及切片 前言什么是Golang&#xff1f;Go语言的基础语法数组声明数组初始化数组访问数组知识点补充 切片定义切片切片初始化len() 和 cap() 函数空(nil)切片切片截取append() 和 copy() 函数知识点补充 前言…

ubuntu20.04安装FTP服务

安装 sudo apt-get install vsftpd# 设置开机启动并启动ftp服务 systemctl enable vsftpd systemctl start vsftpd#查看其运行状态 systemctl status vsftpd #重启服务 systemctl restart vsftpdftp用户 sudo useradd -d /home/ftp/ftptest -m ftptest sudo passwd ftptest…

使用 Python 和蒙特卡罗计算未来股价走势以及历史波动率和隐含波动率

一、简介 预测金融市场是定量精度和全球经济细微差别的复杂融合。在这一探索中,蒙特卡罗模拟脱颖而出,成为首要的统计工具,指导我们对未来股票价格的理解。 这种方法以摩纳哥著名的蒙特卡洛赌场命名,并不依靠运气,而是植根于严格的概率模型。想象一下在受控环境中精心策划…

vue3后台管理框架之技术栈

vue3全家桶技术 基础构建&#xff1a; vue3vite4TypeScript 代码格式 &#xff1a; eslintprettystylelint git生命周期钩子&#xff1a; husky css预处理器&#xff1a; sass ui库&#xff1a; element-plus 模拟数据: mock 网络请求&#xff1a; axios 路由&#xff1a; vue…

SAP MM学习笔记37 - 请求书照合中的 追加请求/追加Credit 等概念/ 请求书的取消

有关请求书照合&#xff0c;之前学习了一部分&#xff0c;现在再来学其中的一些概念。 其实这些概念也许并不常用&#xff0c;但是你又不能不知道&#xff0c;因为客户会问。 有关请求书&#xff0c;贴一些以前学习的文章&#xff0c;以方便阅读。 SAP MM学习笔记33 - 请求书…

38.迪杰斯特拉(Dijkstra)算法

概述 我们在上一篇中面对修路的问题讲述了普利姆算法的实现方式&#xff0c;本篇我们参照迪杰斯特拉算法来对修路问题做进一步拆解。 我们回顾一下之前的问题&#xff1a; “要想富&#xff0c;先修路”&#xff0c;郝乡长最近为了德胜乡修路的事情愁白了头。 得胜乡有A、B、C…

《3D 数学基础》几何检测-最近点

目录 1. 直线上的最近点 2. 射线上的最近点 3. 点到平面的距离 4. 圆或球上的最近点 5. AABB上的最近点 1. 直线上的最近点 q是距离q的最近点&#xff0c;也就是q在直线上的投影。 其中p是直线上的点&#xff08;向量表示&#xff09;&#xff0c;n是直线的法向量&#x…

selenium教程 —— css定位

说明&#xff1a;本篇博客基于selenium 4.1.0 selenium-css定位 element_css driver.find_element(By.CSS_SELECTOR, css表达式) 复制代码 css定位说明 selenium中的css定位&#xff0c;实际是通过css选择器来定位到具体元素&#xff0c;css选择器来自于css语法 css定位优点…

史上最短的“牛熊转换”:BTC价格昨夜起飞,但却来自一条假新闻!

昨夜&#xff0c;加密市场经历了史上最短的一次“牛熊转换”。 在短短10分钟内&#xff0c;BTC快速走出多根阳线&#xff0c;价格直接起飞&#xff0c;连续突破28000美元、29000美元、30000美元的整数关口&#xff0c;最高触及30535.8美元&#xff0c;涨幅近10%&#xff08;数据…

接口自动化测试之HttpRunner测试框架

引言 接口自动化测试的实现方案有很多&#xff0c;没有编程基础的可以使用 PostmanNewman 或 JmeterAnt 来实现&#xff0c;有编程基础的则可以结合自动化测试框架来实现。基于Python的测试框架有&#xff1a;Unittest、HttpRunner、Robot Framework、Pytest等&#xff0c;本文…