C++中的内存管理和模板初识

news2024/11/25 7:10:45

一、内存管理

1.1内存区域的划分

1.1.1内存划分区域图示

1.1.1补:堆和栈都可以进行动态分配和静态分配吗?

不是的,堆无法进行静态分配,只能动态分配;栈可以利用_alloca动态分配,但是分配的空间不能用free或者malloc来释放。

1.1.2几个常见代码在内存区中对应的位置

char char2[] = "abcd" ;

此时char2在 栈区

*char2在 栈区

原因:首先char2为一个数组名,他创建出来就在栈区;字符串虽然是常量字符,但是char2对应的是一个数组,所以其实这一常量字符串被复制到了栈中的数组中,因此*char2得到数组首元素地址,在栈区。

const char* pchar3 = "abcd";

此时pchar3在 栈区

*pchar3在 代码段(常量区)

const修饰的是指向内容,影响到的是*pchar3

原因:pChar3是一个在栈中创建的指针变量,而“abcd”是位于常量区的一个常量字符串,他们之间的关系是pChar3指向常量区中的一个地址,因此*pChar3在常量区

int* ptr1=(int*)malloc(sizeof(int)*4);

此时ptr1在 栈区

*ptr1在 堆区

原因:栈区创建的变量,在堆区申请的空间

const int a = 0;

a在 栈区,但是a具有只读属性。

1.1.2补:

32位系统下,最大的内存访问空间大小为4G,但是不可能把所有内存都给堆用,因此32位系统下堆最大申请4G内存空间的说法是错误的。

1.2C++中的内存申请

1.2.1C中原来的内存申请函数是否可以接着用?

是可以的,包括malloc,realloc,calloc都可以正常使用

1.2.2C++中新增的内存申请方式

使用new和delete进行内存申请/释放

①对于内置类型

使用举例:

//申请
int* p1=new int;
int* p2=new int[10];

//释放
delete p1;
delete[] p2;

默认不对内置类型进行初始化,但可以选择显式初始化

int* p1 = new int(10);
int* p2 = new int[10]{1,2,3,4};

②对于自定义类型

其实c++设计new,更多就是为了自定义类型

假如我们定义了一个class A:

A* a1 = (A*)malloc(sizeof(A));//错误,malloc只会进行内存的申请,不会调用构造,无法初始化

A* a2 = new A;//调用默认构造

A* a3 = new A(2);//调用传入参数2的构造函数
1.2.2补:自定义类型数组快速申请

new很强大,如果我们想快速创建自定义类型的数组,只需要

A* p4 = new A[10];

然后用

delete[] A;

来释放

1.2.3支持隐式类型转换快速参与new的构造

A* p4 = new A[10]{1,2,3,4}//包含隐式类型转换->整形转自定义类型

//当然,构造函数多参数也是支持的
A* p4 = new A[10]{{1,2},{3,4},5,6};

1.2.3补:

若是在创建时创建的是数组,但是释放使用的是delete而不是delete[]的时候

内置类型与未显示析构的自定义类型:可以正常释放,不会出现内存泄漏

经过了显示析构的自定义类型:会导致运行时错误,因为delete只会被调用一次,所以会出现内存泄漏

1.2.4C中有realloc进行扩容,那么C++该如何扩容呢?

一则可以直接使用realloc函数,二则可以选择手动扩容,即自己申请一个空间,再把原空间的内容拷贝过去

重⭐1.2.5malloc/free和new/delete的区别

1.2.5.1用法上:

①malloc/free是函数,new/delete是操作符

②malloc申请不会调用构造函数,但是new会,借此完成初始化;同理free也不会调用析构函数,但是delete会调用

③malloc申请空间需要传空间的大小,但是new不需要,new只要后面跟类型就可以,如果需要创建多个对象只需要在后面加上[]来指定个数即可

④malloc的返回值是void*类型,使用的时候需要强制类型转换,但是new不需要,因为new后面跟的就是类型

⑤malloc使用的时候需要进行判空,但是new不用,只是需要进行异常的捕获

1.2.5.2原理上:

申请自定义类型的时候,malloc/free不会自动调用构造函数和析构函数,只会开辟空间;但是new/delete会在空间开启完成以后调用这两个函数完成初始化/销毁

1.3operator new和operator delete函数

1.3.1他们的本质是什么?

①本质上底层为malloc进行实现

new实际上就是 operator new+构造 来实现

②本质上底层为free进行实现

实际为 operator delete+析构 来实现

1.3.2平时C中我们在写malloc的时候常常会有检查的步骤来确认malloc是否成功,C++中呢?为什么是 operator new+构造 而不是 malloc+构造 呢?

这两个问题的答案是一致的,

因为在C++中,申请空间失败会“抛异常”(跳到catch中)

1.3.3对于A* p4=new A[4]这种多个对象空间申请如何处理?

底层调用operator new[]函数,而operator new[]本质上调用的还是operator new函数,也就是仍旧为malloc的空间

1.3补:二者的调用

operator new和operator delete函数可以显示调用,他们的返回值是void*,传入字节数(类似malloc)

1.4对C++中新申请方式的小结

malloc和free,new和delete,new[]和delete[]互相搭配使用,不要混用

原因举例:例如当我们使用new T[]进行空间申请的时候(T为代号,可以为内置类型或者自定义类型)

在函数调用层面:

①内置类型或不显示析构的自定义类型,在向operator new[]函数传参的时候传正常size的总字节数

②进行了显示析构的自定义类型会加上前面4个字节的大小,而这四个字节是为了确定要调用多少次析构,此时传size+4的字节数,汇编层面

在②这种情况下与free/delete混用会导致程序崩溃

1.5定位new表达式(placement——new)(这是一个规定)

1.5.1有何作用?

对已经申请好的空间,帮助显示调用构造函数

①使用举例1

例如想创建自定义类型A的指针并给空间(不用new)

A* p1 = (A*)operator(sizeof(A));

p1->A();//这是错误的,不支持这样显式调用构造

new(p1)A;//这是定位new表达式,作用就是调用构造

new(p1)A(10);//定位new表达式,构造函数传入参数10

②使用举例2-》用以参考

A* p2 = (A*)operator new[](sizeof(A) * 10);
for(int i=0;i<10;i++)
    new(p2+i)A(i);
for(int i=0;i<10;i++)
    (p2+i)->~A();

operator delete[](p2);

1.5.2何时需要用?

这涉及到内存池:

如果T为自定义类型,一则可以new T[n],默认直接找堆;如果要从内存池申请n个对象那要用到定位new表达式来构造。

二、模板初识

2.1泛型编程

试想,在C语言中我们要多次交换两数的值,而且这些数还有许多不同的类型,我们要怎么办?

是的,只能一个一个去写。

可是他们的逻辑十分相似,因此我们希望可以快捷一些:

写一个框架,针对广泛的类型,而这一思想就是泛型编程,这个框架就是函数模板。

2.2函数模板

2.2.1函数模板格式

template <typename T1,typename T2>
//或者template <class T1,class T2>

此处的T1和T2就是类型的泛型名字

使用举例:

template <typename T>
void Swap(typename T& left,typename T& right)
{
    T temp=left;
    left=right;
    right=temp;
}

之后,主函数中

double a2=1.1,b2=1.3;
int a1=1,b1=3;

Swap(a2,b2);
Swap(a1,b1);

2.2.2同一模板下不同类型调用的是同一个函数吗?

我们若是逐步运行,会发现函数调用都会回到模板中,所以这说明调用的都是同一函数吗?

不,眼见在这里并不真实,他们类型的大小都相同,怎么会是同一个函数呢?

我们把目光放到汇编层次,可以发现找的是函数Swap<int>和Swap<double>这两个不同地址处的函数

总的来说,编译器会根据传参的类型推演出所需要的函数,自动实现他们,方便链接时配对。

2.2.2补:

举的例子里涉及到交换函数,平时我们很常用,所以在std库里其实已经有了一个对应的函数模板,可以直接

swap(a1,b1);
swap(a2,b2);

2.2.3模板的推演

假设我们有这样一个函数模板

template <class T>
T Add(T& a,T& b)
{
    return a+b;
}

此时我传参

int a1=10;
double b1=10.2;

会发现程序报错了。

为什么?我们该如何解决这一问题呢?

原因是根据函数模板推演的时候存在歧义。

有三种解决方案

①可以强转赋值

Add((double)a1,b1);
Add(a1,(int)b1);

②显式模板实例化,不进行推演

Add<int>(a1,b1);
Add<double>(a1,b1);

③可以设置两个模板参数,利用auto来自动获取返回值类型

template <class T1,class T2>
auto Add(const T1& a,const T2& b)
{
    return a+b;
}

使用时

Add(a1,b1);

即可

2.2.4必须使用显示模板实例化的情况

之前举的例子可以通过参数对模板T的类型进行推演,但如果无法推演呢?

例如

templat <class T>
T* func(int a)
{
    //...
}

此时则必须要有显示模板实例化

int* ret = func<int>(1);

2.2.5模板与普通函数可以同时存在

例如模板

template <class T1,class T2>
auto Add(const T1& a,const T2& b)
{
    return a+b;
}

与函数

int Add(int a,int b)
{
    return a+b;
}

在选择进行调用的时候遵循“有现成的吃现成的”,即

先调用普通函数在调用模板,若没有普通函数,掉对应模板,如果没有对应模板,进行隐式类型转换也会完成调用

2.3类模板

2.3.1作用

假设我在C语言中实现了一个栈(Stack),此时我希望创建两个栈类型的变量st1和st2,一个存int,一个存double,我该怎么办?

我需要写两个仅DataType类型不同的结构体,那可否简化?

可用类模板

templat <class T>
class Stack
{
public:
    //...完成栈的定义
}

此时T可以表示类型

2.3.1补

类模板和函数模板都是不提高效率,只简化代码,变量st1与st2的类型并不相同

2.3.2类模板的实例化

函数模板尚有推演的可能,但是类模板一点也没有,所以必须进行实例化

Stack<int> st1;
Stack<double> st2;

2.3.3类模板中的声明与定义分离

类模板的声明和定义分离相较于普通类要修改格式

template <class T>//每一个声明定义分离的函数之前都要加上这一行
void Stack<T>::Push(const T& data)//域访问限定符之前要写完整的类类型
{
    //...;
}

且不可以分离文件进行定义(.cpp与.h也不可以),会报链接错误

2.4模板注意事项

①模板运行时不检查数据类型,也不保证类型安全,仅仅相当于类型的宏替换

②模板可以用来创建动态增长的数据结构(模板类型的大小可变)

③模板的实参:模板实例化的内容

④模板参数可以为类型,也可以为非类型,例如

template <class T,size_t N>

其中非类型其实相当于typedef

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

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

相关文章

基于Logistic-Map混沌序列的数字信息加解密算法matlab仿真,支持对文字,灰度图,彩色图,语音进行加解密

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于Logistic-Map混沌序列的数字信息加解密算法matlab仿真,系统包含GUI操作界面&#xff0c;系统支持对文字,灰度图,彩色图,语音进行加解密。 2.测试软件版本以及…

项目运行插件-日志管理

日志管理 项目运行时模块提供了项目日志收集&#xff0c;检索和保存查询方案等功能。 体验地址&#xff1a; http://119.163.197.219:13456/view/runtime/index.html#/log/aioLogPage 沟通加QQ群 &#xff1a; 908377977 gitee 开源地址 &#xff1a; https://gitee.com/aio…

打印文档时,只有图片中的文本不清晰该如何处理

最近打印东西的时候&#xff0c;发现只有图片中的文本并不清晰&#xff0c;就想研究一下如何改善这个问题。 打印机是佳能的 MF113w&#xff0c;一个不错的多功能激光黑白打印机&#xff0c;支持无线打印。唯一问题就是每次 DHCP 分配 IP 到期后&#xff0c;这款打印机就会亮错…

AI提质增效率赋能工业产品质检,基于高精度YOLOv5全系列参数【n/s/m/l/x】模型开发构建工业生产场景下PCB电路板缺陷问题智能化分割检测识别分析系统

在PCB电路板的生产制造过程中&#xff0c;质量检测是确保产品质量、维护品牌形象的关键环节。然而&#xff0c;传统的人工检测方式依赖于经验丰富的工人师傅通过光学显微镜等设备进行逐块检查&#xff0c;这不仅劳动强度大、效率低下&#xff0c;而且受限于人的主观判断、视力疲…

《华为 eNSP 模拟器安装教程》

1.电脑安装环境要求&#xff1a; 检查电脑是否安装过 eNSP 和依赖软件&#xff0c;如果有&#xff0c;请全部卸载。 安装软件列表&#xff1a; 2.软件安装&#xff1a; 安装 WinPcap&#xff1a; 打开安装包&#xff0c;单击【Next】 单击【I Agree】 单击【Install】 单击【…

《信息系统安全》课程实验指导

第1关&#xff1a;实验一&#xff1a;古典密码算法---代换技术 任务描述 本关任务&#xff1a;了解古典密码体制技术中的代换技术&#xff0c;并编程实现代换密码的加解密功能。 注意所有明文字符为26个小写字母&#xff0c;也就是说字母表为26个小写字母。 相关知识 为了完…

1、常用的数据库、表操作

基本的建表和数据库拷贝操作。 一、数据定义语言DDL show databases; # 查看全部数据库 show create database db; # 查看数据库db create database db; # 创建数据库db drop database db; # 删除数据库db use db; # 使用数据库db基本…

1 Linux SSH安全加固_linux system-auth

![在这里插入图片描述](https://img-blog.csdnimg.cn/20201117150524918.png?x-oss-processimage/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwOTA3OTc3,size_16,color_FFFFFF,t_70#pic_center) ![在这里插入图片描述](https://…

11、LLaMA-Factory自定义数据集微调

1、数据集定义 针对实际的微调需求&#xff0c;使用专门针对业务垂直领域的私有数据进行大模型微调才是我们需要做的。因此&#xff0c;我们需要探讨如何在LLaMA-Factory项目及上述创建的微调流程中引入自定义数据集进行微调。**对于LLaMA-Factory项目&#xff0c;目前仅支持两…

什么是 Grafana?

什么是 Grafana&#xff1f; Grafana 是一个功能强大的开源平台&#xff0c;用于创建、查看、查询和分析来自多个来源的数据。通过可视化仪表盘&#xff08;Dashboard&#xff09;&#xff0c;它能够帮助用户监控实时数据、生成历史报告&#xff0c;甚至进行预测分析。Grafana…

深入理解Java虚拟机:Jvm总结-类文件结构以及类加载机制

第六章 类文件结构 6.1 意义 代码编译的结果从本地机器码转变为字节码&#xff0c;冲破了平台界限。 6.2 无关性的基石 实现语言无关性的基础仍然是虚拟机和字节码存储格式。Java虚拟机不与包括Java语言在内的任何程序语言绑定&#xff0c;它只与“Class文件”这种特定的二…

vue2实践:el-table实现由用户自己添加删除行数的动态表格

需求 项目中需要提供一个动态表单&#xff0c;如图&#xff1a; 当我点击添加时&#xff0c;便添加一行&#xff1b;点击右边的删除时&#xff0c;便删除这一行。 至少要有一行数据&#xff0c;但是没有上限。 思路 这种每一行的数据固定&#xff0c;但是不定行数的&#x…

校园水电费管理|基于java的校园水电费管理小程序系统 (源码+数据库+文档)

校园水电费管理 目录 基于java的校园水电费管理小程序系统 一、前言 二、系统设计 三、系统功能设计 小程序端 后台功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕…

Selenium使用浏览器用户配置进行测试

本文主要介绍了如何在使用Selenium WebDriver进行自动化测试时&#xff0c;创建和使用自定义的Firefox配置文件。 什么是Firefox配置文件&#xff1f; Firefox会将用户的个人信息&#xff0c;如书签、密码和用户偏好设置存储在一个称为配置文件的文件集合中&#xff0c;这些文…

C++设计模式——Iterator迭代器模式

一&#xff0c;迭代器模式的定义 迭代器模式是一种行为型设计模式&#xff0c;它使得遍历一个容器对象中的元素变得更加简单。 迭代器模式将遍历操作从容器对象&#xff08;如集合、列表&#xff09;中分离出来&#xff0c;它通过迭代器对象来遍历容器对象中的元素&#xff0…

若依后端正常启动但是uniapp移动端提示后端接口异常

pc端能用模拟器也能正常连接接口&#xff0c;手机端真机调试连不上接口 解决&#xff1a; 1. 先看config.js的 填自己的ip地址 module.exports { // baseUrl: https://vue.ruoyi.vip/prod-api, baseUrl: "http://192.168.101.5:8080", } 2.网络环境问题&#…

mysql -小计

//表单某字段值为当前打开文档Id (function () { var rdoc getRelateDocument(); var warehouseName rdoc.getItemValueAsString(“warehouseName”); var name rdoc.getItemValueAsString(“name”); var color rdoc.getItemValueAsString(“color”); var batchNumber r…

2024年Web前端JavaScript面试题整理附答案

&#xff08;1&#xff09;两等号判等&#xff0c;会在比较时进行类型转换&#xff1b; &#xff08;2&#xff09;三等号判等(判断严格)&#xff0c;比较时不进行隐式类型转换&#xff0c;(类 型不同则会返回false)&#xff1b; &#xff08;3&#xff09;Object.is 在三等号…

基于风力发电系统的开关磁阻Simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于风力发电系统的开关磁阻Simulink建模与仿真&#xff0c;开关磁阻风力发电系统&#xff08;Switched Reluctance Wind Power Generation System&#xff09;利用开关磁阻电…

计算机的错误计算(八十九)

摘要 探讨反双曲余切函数 acoth(x) 在 附近的计算精度问题。 Acoth(x) 函数的定义为&#xff1a; 其中 x 的绝对值大于 1 . 例1. 计算 acoth(1.000000000002) . 不妨在 Excel 的单元格中计算&#xff0c;则有&#xff1a; 若在Python中用定义直接计算&#xff0c;则有几乎…