当前位置: 首页 > news >正文

内存管理 + 模版初阶

内存管理 + 模版初阶

  • 一、内存分布
  • 二、new 和 delete
  • 二、new 和 delete的实现原理
  • 四、定位 new
  • 五、模板
    • 5.1 函数模板
      • 5.1.1函数模板的格式
      • 5.1.2函数模板实例化
      • 5.1.3函数模板匹配规则
    • 5.2 类模板

一、内存分布

  • 栈区:用来存储局部数据。栈区是向下生长的。
  • 内存映射段:是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口
    创建共享内存,做进程间通信。
  • 堆区:用于动态内存分配。堆区是向上生长的。
  • 代码段:存储可执行的代码和只读常量。
  • 数据段:存储全局数据和静态数据。

二、new 和 delete

  • newdelete都是关键字,并不是函数。它们的作用是向堆区申请和释放资源。
  • new 和 delete 对于内置类型:与malloc / calloc 、free类似,但是new 和 delete更加简洁,同时能够初始化。(malloc/calloc申请空间失败会返回空指针,而new申请空间失败会抛异常)。
#include<iostream>usingnamespacestd;voidtest1(){//场景1://申请一个整型类型的空间//以前的写法int*ptr1=(int*)malloc(sizeof(int));if(ptr1==nullptr){perror("malloc fail");exit(1);}//如果要求同时初始化,就应该使用calloc函数,但是只能全部初始化为0//释放空间free(ptr1);ptr1=nullptr;//新的写法// new 数据类型int*ptr2=newint;//如果要求同时初始化// new 数据类型(初始化值)int*ptr3=newint(1);//释放空间deleteptr2;deleteptr3;//////////////////////////////////////////////////////////////////////////////////////////////场景2://申请一个连续空间的整型数组//以前写法int*p1=(int*)malloc(sizeof(int)*4);if(p1==nullptr){perror("malloc fail");exit(1);}//释放空间free(p1);p1=nullptr;//新的写法// new 数据类型[数据个数]int*p2=newint[4];//如果要求同时初始化// new 数据类型[数据个数] {初始化值}int*p3=newint[4]{1,2,3,4};for(inti=0;i<4;i++){cout<<*(p3+i);}cout<<endl;//释放空间delete[]p2;delete[]p3;}intmain(){test1();return0;}
  • 这里要明白new 和 delete的使用方法。同时申请的是连续空间,应该使用delete[ ] 进行释放空间,保证配套使用。
  • C++之所以创建new和delete,是为了解决自定义类型。
#include<iostream>usingnamespacestd;classA{public:A(int_a=0){cout<<"A()"<<endl;}~A(){cout<<"~A()"<<endl;}private:int_a;};voidtest2(){//malloc实现A*ptr1=(A*)malloc(sizeof(A));if(ptr1==nullptr){perror("malloc fail");exit(1);}//new实现A*ptr2=newA;//释放空间free(ptr1);ptr1=nullptr;deleteptr2;}

可以发现new和delete会自动调用构造函数和析构函数,而malloc不会。

voidtest(){//支持初始化A*ptr3=newA(1);A*ptr6=newA(2);//自定义类型的数组A*ptr4=newA[2];//自定义类型数组的初始化A*ptr5=newA[2]{1,2};delete[]ptr5;//拷贝构造来初始化A*ptr7=newA[2]{*ptr3,*ptr6};//匿名对象初始化A*ptr8=newA[2]{A(1),A(2)};delete[]ptr8;delete[]ptr7;delete[]ptr4;deleteptr3;deleteptr6;}

二、new 和 delete的实现原理

  • new 的实现原理

1.调用operator new()函数来实现申请空间资源。

operator new()也是通过malloc来实现的,但是检查申请空间是否成功,并不是看返回的指针是否为nullptr,而是通过抛异常来实现的。

2.调用析构函数来进行初始化。

  • new[T] 的实现原理

1.调用operator new[ ],operator new[ ] 中调用operator new()完成对T个对象空间的申请,同时还会在申请空间的初始位置额外开一块空间(用来存放T,以便析构时知道析构多少空间),返回偏移后的指针。
2.调用T次构造函数,完成对对象空间的初始化。

  • delete的实现原理

1.调用析构函数,对自定义类型中的类成员进行空间释放。
2.调用operator delete()函数 ,实现对自定义类型的空间释放。

operator delete()也是通过free来释放内存空间的。

  • delete[ ]实现原理

1.指针偏移读取到要析构T次,然后调用T次析构函数,完成对对象空间内申请资源的成员的空间的释放。
2.调用operator delete[ ] ,operator delete[ ] 调用operator delete()完成对T个对象空间的释放。

四、定位 new

定位new是对已经分配好空间的内存调用析构函数进行初始化。

格式: new(place_adress)type 或者是 new(place_adress)type(initializer-list)
  • place_adress -> 分配空间的地址
  • type -> 数据类型
  • initialzer-list 初始化列表

常应用于内存池方面。

voidtest3(){A*ptr1=(A*)malloc(sizeof(A));new(ptr1)A(1);ptr1->~A();free(ptr1);A*ptr2=(A*)operatornew(sizeof(A));new(ptr2)A(3);ptr2->~A();operatordelete(ptr2);}

重点:malloc / free 和 new / delete 的区别

  • malloc申请空间失败,返回NULL;new不需要,new需要捕获异常。
  • 申请自定义类型空间时,malloc/free只会开辟和释放空间,不会调用构造函数和析构函数。new会在开辟好空间后,调用构造函数进行初始化,delete会在释放空间之前会调用析构函数,对对象内申请资源进行释放。
  • malloc/free 是函数,而new / delete是操作符。
  • malloc对申请的空间不会初始化,new会对申请的空间进行初始化。
    malloc返回void类型,new后面跟着数据类型,会返回该类型的指针。
  • malloc需要手动计算申请空间大小,new不需要,new后面有数据类型,多个对象,只需在[ ]内明确对象个数。

五、模板

模板分为函数模板类模板
模板不建议声明和定义分离,会出现链接错误。

5.1 函数模板

5.1.1函数模板的格式

template<typename T1,typename T2,……,typename Tn> 返回类型 函数名(参数列表) {}
template<classT>voidswap(constT&x,constT&y){T tmp=x;x=y;y=tmp;}
  • typename 是关键字,意思是类型名,也可以用class来代替。
  • T是指数据类型

5.1.2函数模板实例化

函数模板示例化分为隐式实例化显式实例化
隐式实例化:通过实参,编译器来推断数据类型。

template<classT>TAdd(constT&x,constT&y){return(x+y);}intmain(){cout<<Add(2,3)<<endl;;//自动推导T类型为intcout<<Add(1.2,3.4)<<endl;//自动推导T类型为double/* Add(1.2,3); 这样的语句会报错,编译器推导不出T的类型到底是int还是double */return0;}

显式实例化:手动来确定类型。

template<classT1,classT2>T2Add(constT1&x,constT2&y){return(x+y);}intmain(){cout<<Add<int,double>(1,2.2)<<endl;return0;}

如果实参类型与手动设置类型不一致,会进行类型转化,如果不能类型转换,就会报错。

5.1.3函数模板匹配规则

1. 非模板函数与函数函数可以以相同名字存在,同时也允许模板函数的实例化与非模板函数一致。
2. 非模板函数与函数模板实例化后一致,那么在调用函数时,优先使用非模板函数。如果函数模板能够产生更加匹配的函数,那么就会使用函数模板。

template<classT1,classT2>T2Add(constT1&x,constT2&y){return(x+y);}doubleAdd(constintx,constdoubley){returnx+y;}intmain(){cout<<Add(1,2.2)<<endl;return0;}

3.函数模板不会类型转化,而普通函数会类型转化。

5.2 类模板

格式: template<typename T1,typename T2,……,typename Tn> class 类名 { //类内成员定义 }

类模板实例化:需要用<>指定类型。

template<classT>classStack{public:Stack(intn=4){_arr=newT[n];_size=0;_capacity=n;}~Stack(){delete[]_arr;_arr=nullptr;_size=_capacity=0;}private:T*_arr;int_size;int_capacity;};intmain(){Stack<int>s1;Stack<char>s2;}
http://www.cnnetsun.cn/news/4281276.html

相关文章:

  • 自媒体工具怎么选?从功能、价格、安全性三个维度对比
  • 【PYTHON】模拟请求接口
  • ROS2机器人建模仿真实战:从URDF到Gazebo的完整链路
  • MySQL安装与Navicat连接指南:破解版风险与免费替代方案
  • 数学建模竞赛中MATLAB微分方程符号解实战:从dsolve使用到论文写作
  • WinForm集成PaddleOCR v3:ONNX Runtime C#部署实战
  • 单片机毕业设计-语音识别与红外满溢检测智能垃圾分类装置研发 基于 LU-ASR01 的四分类智能垃圾桶硬件系统设计(013105)
  • Yolo 小白入门 29:训练前先验货——用可视化揪出错框、错类和空标签
  • 单片机毕业设计-基于 STM32 的便携式人体健康监测终端及 APP 开发 基于 STM32 的多生理信号采集与声光报警系统设计(013205)
  • 仿WX即时聊天源码深度拆解:架构、消息链路与音视频部署
  • CIMPro 孪大师分层开发实战:从零代码速建到深度定制的全场景指南
  • 工业自动化通信基石:Profinet GSD文件深度解析与汇川SV660F配置实战
  • ESP32 DAC音频输出实战:从硬件设计到软件驱动的完整指南
  • Agentic 工作流重塑出行预测:多智能体协同与多模态大模型的深度实践
  • Java面经:从八股到实战,复盘面试官真正在考什么
  • GPT-6传闻下的OpenAI API接入实战指南
  • 代码跑通之后怎么提升?模型改进、损失函数调优与实验管理完整指南
  • OpenAI高管离职潮背后:技术路线、AI安全与组织治理的深层博弈
  • OpenClaw部署实战:从安装到本地模型与Skill开发
  • 没有眼睛的AI,为什么能教你怎么戴美瞳?大模型知识表征与能力边界解析
  • QT实现视觉引导机械臂闭环抓取的工程实践
  • GLM-5.2登陆Mistral平台:模型托管与API接入工程实践指南
  • 留学生求职服务机构可信度评估研究 ——基于可验证资质的实证分析
  • 2027北京机器人展聚焦机器人出海合规,助力国产装备走向全球
  • Java工程师能力评估指南:从HashMap到JVM,面试官视角的实战自查清单
  • Windows 0xC0000142 启动失败怎么修?先查出错模块,再用软领DLL系统修复运行库
  • 多模态模型Diffing:表征差异分析与特征控制实战
  • MSK+LDPC+扩频通信链路仿真:参数耦合与工程落地详解
  • ISO15118协议Schema文件包本地化实践:解决网络依赖与开发集成
  • 基于SpringBoot+DeepSeek的智能康养助手的设计与实现(源码+文档+部署+讲解)