【语法】C++的内存管理 模板

内存管理:

在C语言中,动态开辟空间可以用malloc,calloc,realloc这三个函数,下面先来复习一下这三者的区别

malloc和calloc都是用来开辟新空间,calloc在malloc的基础上还会初始化该空间为0,用法也不同

   malloc(sizeof(int)*n)       calloc(sizeof(int),n)   

realloc时用来给已经动态开辟好的空间扩容的具体用法示例如下

int* newp = (int*)realloc(p,sizeof(int)*n)

C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过newdelete操作符进行动态内存管理。

void Test()
{// 动态申请一个int类型的空间int* ptr4 = new int;// 动态申请一个int类型的空间并初始化为10int* ptr5 = new int(10);// 动态申请10个int类型的空间int* ptr6 = new int[3];delete ptr4;delete ptr5;delete[] ptr6;
}

可以发现new和我们之前用的函数都有点不一样,这是因为它是操作符 ,所以导致了它独特的使用方法。

要特别注意new int(10)不是申请10个int空间,而是申请一个int空间并初始化为10,new int[10]才是申请10个int空间,但申请数组就不能初始化了

那new和malloc有什么区别呢?

在回答这个问题之前,我们先来看另外一组操作符

operator new和operator delete

尽管他们有operator,但这和类的那些重载函数不一样,,operator new 和operator delete是系统提供的全局函数,用法和malloc相似

void Test()
{// 动态申请一个int类型的空间int* ptr4 = (int*)operator new(sizeof(int));// 动态申请10个int类型的空间int* ptr6 = (int*)operator new(sizeof(int)*3);operator delete(ptr4);operator delete[](ptr6);
}

下面是operator new的底层实现

/*
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 new也是调用的malloc,只不过在这基础上如果开辟失败会抛异常

operator delete 和free没区别,因为释放空间失败直接终止进程,operator delete只是为了和operator new成对出现

除此之外,operator new和operator delete也是可以重载的,如果你想在开辟空间的基础上做别的事情,就可以重载operator new/delete

new,operator new,malloc

上面将了operator new和malloc的区别,是在调用malloc的基础上可以抛异常。

而new和operator new的区别则是在operator new的基础上会调用自定义类型的构造函数,这是因为new 的原理就是

1.调用operator new函数申请空间 

2.在申请的空间上执行构造函数

 delete也相似

1.在空间上执行析构函数

2. 调用operator delete函数释放空间 

 那既然new是在operator new的基础上调用构造函数,有没有什么办法可以直接在operator new 的基础上自己去调用构造函数呢?

有的兄弟,有的,这个时候就需要请出定位new

当我们已经通过malloc或operator new申请了空间后,如果想要调用该类的构造函数

Date *p = (Date*)operator new(sizeof(Date));
new(p) Date;

new后面的括号里面是指针名称,后面跟类的名称

正常来说是不能显式调用构造函数的,构造函数都是在实例化类时隐式调用的,而定位new就算是一种显式调用

总结:

new和malloc的区别

  1.  new会调用构造函数,失败抛异常,malloc失败了返回0  
  2.  malloc是一个函数,new是一个操作符
  3. malloc用法:参数传字节数,返回值是void*;new后面跟申请对象的类型,返回值是类型的指针

malloc,operator new,new的关系

  1. malloc
  2. operator new -> malloc + 失败抛异常
  3. new               -> operator new + 构造函数

读者有没有想过一个问题,为什么会有free,delete这样的函数,有什么意义呢?

大家都知道free和delete都是用来释放之前在中动态开辟的空间的,那如果不释放有什么危害?

答案是会造成内存泄漏,虽说对小程序没有什么危害,但对长期运行的程序,出现内存泄露危害很大,或者设备内存本身很小,也有危害,要防止可以用工具检测或智能指针

 模板:

大家都知道,重载可以让同一个函数名存在多个,根据参数的不同来选择对应的函数,有些时候函数内部除了类型不同没有任何的区别,这时用重载就会非常麻烦,就会用到模板(泛型编程)

模板分为函数模板和类模板

先来看下面一个Swap的函数模板

template<class T>//<typename T>也可以
void Swap( T& left, T& right)
{T temp = left;left = right;right = temp;
}

这是一个可以用于任何类型的Swap函数模板,其实就是在函数的上面一行写上了template<class T>

之后不管调用Swap时是什么类型,都可以执行,但它执行的是这个函数模板吗?

当然不是,函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。

也就是说模板本身并不会去执行,是在预处理阶段生成对应的函数/类,真正去执行的是生成的函数/类

可以看到,三次调用的Swap的地址是不一样的,所以是生成了三个不同参数的Swap,再分别调用

看上面这个代码,可以看到模板的最后一行没有加分号,但这并不影响编译通过,这也证明了模板并不会参与编译,只要没有用到这个函数,预处理阶段就不会生成相应的函数。

显式实例化和隐式实例化

模板的实例化分为显式和隐式,如Swap(a,b)就是隐式, 由编译器自己去判断应该调用什么类型的函数,也可以Swap<int>(a,b),这样就是显式实例化,指定编译器必须要用int类型的函数。

如果a是int型,b是double型,Swap(a,b)也会报错,因为不知道该生成那种函数,这时有两种解决方法

  1. Swap(a,(int)b),这样让b也变成int类型,编译器就知道该实例化生成一个int类型的Swap函数
  2. Swap<int>(a,b),直接显式实例化也可以让编译器知道该实例化生成一个int类型的Swap函数

而类模板在实例化时必须显式实例化,隐式实例化会报错

并且函数模板和同名的函数可以共存

// 专门处理int的加法函数
int Add(int left, int right)
{return left + right;
}
// 通用加法函数
template <class T>
T Add(T left, T right)
{return left + right;
}
void Test()
{Add(1, 2);      // 与非模板函数匹配,编译器不需要特化Add<int>(1, 2); // 调用编译器特化的Add版本
}

有人可能会问,第一个Add会调用函数模板生成的函数还是另一个同名函数呢?

答案是同名函数。对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板

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

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

相关文章

30~32.ppt

目录 30.导游小姚-介绍首都北京❗ 题目​ 解析 31.小张-旅游产品推广文章 题目 解析 32.小李-水的知识❗ 题目​ 解析 30.导游小姚-介绍首都北京❗ 题目 解析 新建幻灯片-从大纲-重置-检查设计→主题对话框→浏览主题&#xff1a;考生文件夹&#xff08;注意&#x…

uniapp实现人脸识别(不使用三方插件)

uniapp实现人脸识别 内容简介功能实现上传身份证进行人脸比对 遇到的问题 内容简介 1.拍摄/相册将身份证照片上传到接口进行图片解析 2.使用live-pusher组件拍摄人脸照片&#xff0c;上传接口与身份证人脸进行比对 功能实现 上传身份证 先看下效果 点击按钮调用chooseImage…

Evaluating Very Long-Term Conversational Memory of LLM Agents 论文

Abstract : 长期开放域对话的现有作品着重于评估不超过五个聊天会议的上下文中的模型响应。尽管LongContext大语言模型&#xff08;LLM&#xff09;和检索增强发电&#xff08;RAG&#xff09;技术的进步&#xff0c;但在长期对话中的功效仍未得到探索。为了解决这一研究差距&a…

相对收益-固定收益组合归因-Campisi模型

固定收益组合归因-Campisi模型 1 Campisi模型11.1 Campisi归因框架1.2 Campisi模型绝对收益分解1.2.1 票息收益1. 2.2 收敛收益1. 2.3 骑乘收益1. 2.4 平移收益1. 2.5 扭曲收益1. 2.6 利差收益1. 2.7 残差收益 1.3 Campisi模型超额收益分解 2 Campisi模型22.1 分解框架2.2 模型…

IntelliJ IDEA使用经验(十三):使用Git克隆github的开源项目

文章目录 问题背景办法1、设置git代理&#xff1b;2、再次克隆项目&#xff1b;3、再次按常规方式进行git克隆即可。 问题背景 由于github在国外&#xff0c;很多时候我们在使用idea克隆开源项目的时候&#xff0c;没办法检出&#xff0c;提示 连接重置。 办法 1、设置git代…

JAVA安全之Java Agent打内存马

基本介绍 Java Agent是一种特殊的Java程序&#xff0c;它允许开发者在Java虚拟机(JVM)启动时或运行期间通过java.lang.instrument包提供的Java标准接口进行代码插桩&#xff0c;从而实现在Java应用程序类加载和运行期间动态修改已加载或者未加载的类&#xff0c;包括类的属性、…

RabbitMQ 消息顺序性保证

方式一&#xff1a;Consumer设置exclusive 注意条件 作用于basic.consume不支持quorum queue 当同时有A、B两个消费者调用basic.consume方法消费&#xff0c;并将exclusive设置为true时&#xff0c;第二个消费者会抛出异常&#xff1a; com.rabbitmq.client.AlreadyClosedEx…

【MQ】Spring3 中 RabbitMQ 的使用与常见场景

一、初识 MQ 传统的单体架构&#xff0c;分布式架构的同步调用里&#xff0c;无论是方法调用&#xff0c;还是 OpenFeign 难免会有以下问题&#xff1a; 扩展性差&#xff08;高耦合&#xff0c;需要依赖对应的服务&#xff0c;同样的事件&#xff0c;不断有新需求&#xff0…

EasyExcel 导出合并层级单元格

EasyExcel 导出合并层级单元格 一、案例 案例一 1.相同订单号单元格进行合并 合并结果 案例二 1.相同订单号的单元格进行合并2.相同订单号的总数和总金额进行合并 合并结果 案例三 1.相同订单号的单元格进行合并2.相同订单号的商品分类进行合并3.相同订单号的总数和总金额…

cs106x-lecture3(Autumn 2017)

打卡cs106x(Autumn 2017)-lecture3 1、streamErrors Suppose an input file named streamErrors-data.txt contains the following text: Donald Knuth M 76 Stanford U. The code below attempts to read the data from the file, but each section has a bug. Correct th…

C++模板编程——typelist的实现

文章最后给出了汇总的代码&#xff0c;可直接运行 1. typelist是什么 typelist是一种用来操作类型的容器。和我们所熟知的vector、list、deque类似&#xff0c;只不过typelist存储的不是变量&#xff0c;而是类型。 typelist简单来说就是一个类型容器&#xff0c;能够提供一…

windows通过网络向Ubuntu发送文件/目录

由于最近要使用树莓派进行一些代码练习&#xff0c;但是好多东西都在windows里或虚拟机上&#xff0c;就想将文件传输到树莓派上&#xff0c;但试了发现u盘不能简单传送&#xff0c;就在网络上找到了通过windows 的scp命令传送 前提是树莓派先开启ssh服务&#xff0c;且Window…

字节跳动后端一面

&#x1f4cd;1. Gzip压缩技术详解 Gzip是一种流行的无损数据压缩格式&#xff0c;它使用DEFLATE算法来减少文件大小&#xff0c;广泛应用于网络传输和文件存储中以提高效率。 &#x1f680; 使用场景&#xff1a; • 网站优化&#xff1a;通过压缩HTML、CSS、JavaScript文件来…

三维模拟-机械臂自翻车

机械仿真 前言效果图后续 前言 最近在研究Unity机械仿真&#xff0c;用Unity实现其运动学仿真展示的功能&#xff0c;发现一个好用的插件“MGS-Machinery-master”&#xff0c;完美的解决了Unity关节定义缺少液压缸伸缩关节功能&#xff0c;内置了多个场景&#xff0c;讲真的&…

USB子系统学习(四)用户态下使用libusb读取鼠标数据

文章目录 1、声明2、HID协议2.1、描述符2.2、鼠标数据格式 3、应用程序4、编译应用程序5、测试6、其它 1、声明 本文是在学习韦东山《驱动大全》USB子系统时&#xff0c;为梳理知识点和自己回看而记录&#xff0c;全部内容高度复制粘贴。 韦老师的《驱动大全》&#xff1a;商…

史上最快 Python版本 Python 3.13 安装教程

Python3.13安装和配置 一、Python的下载 1. 网盘下载地址 (下载速度比较快&#xff0c;推荐&#xff09; Python3.13.0下载&#xff1a;Python3.13.0下载地址&#xff08;windows&#xff09;3.13.0下载地址&#xff08;windows&#xff09; 点击下面的下载链接&#xff0c…

AWS Fargate

AWS Fargate 是一个由 Amazon Web Services (AWS) 提供的无服务器容器计算引擎。它使开发者能够运行容器化应用程序&#xff0c;而无需管理底层的服务器或虚拟机。简而言之&#xff0c;AWS Fargate 让你只需关注应用的容器本身&#xff0c;而不需要管理运行容器的基础设施&…

vue3+vite+eslint|prettier+elementplus+国际化+axios封装+pinia

文章目录 vue3 vite 创建项目如果创建项目选了 eslint prettier从零教你使用 eslint prettier第一步&#xff0c;下载eslint第二步&#xff0c;创建eslint配置文件&#xff0c;并下载好其他插件第三步&#xff1a;安装 prettier安装后配置 eslint (2025/2/7 补充) 第四步&am…

vLLM V1 重磅升级:核心架构全面革新

本文主要是 翻译简化个人评读&#xff0c;原文请参考&#xff1a;vLLM V1: A Major Upgrade to vLLM’s Core Architecture vLLM V1 开发背景 2025年1月27日&#xff0c;vLLM 开发团队推出 vLLM V1 alpha 版本&#xff0c;这是对框架核心架构的里程碑式升级。基于过去一年半的…

Jupyter Notebook自动保存失败等问题的解决

一、未生成配置文件 需要在命令行中&#xff0c;执行下面的命令自动生成配置文件 jupyter notebook --generate-config 执行后会在 C:\Users\用户名\.jupyter目录中生成文件 jupyter_notebook_config.py 二、在网页端打开Jupyter Notebook后文件保存失败&#xff1b;运行代码…