C++内存管理和模板

文章目录

  • 内存管理
    • new和delete
  • 函数模板
    • 隐式实例化
    • 显式实例化
  • 类模板

内存管理

有时候我们需要动态的申请内存,比如队列,栈,二叉树等数据结构,我们一开始并不知道要存储多少个数据,也就是不确定究竟要多大的内存,给小了存不下,给大了会造成内存资源的浪费,因此动态的申请内存是必要的,即需要多少申请多少。
那么在C语言阶段我们不是已经学过malloc,calloc,realloc,free这几个动态开辟内存的函数了吗?为什么C++中还要引入新的,其中一个重要原因就是,C语言的不可以对动态开辟的内存进行任意的初始化,特别是对于自定义类型来说。接下来我们引入两个新的关键字,new和delete。

new和delete

	int* p1 = (int*)malloc(sizeof(int));
	int* p2 = new int;

	free(p1);
	delete p2;

对于内置类型来说new和delete与malloc和free除了用法上不一样,本质上并没有什么区别,都不会对新开辟的内存进行初始化。
在这里插入图片描述
在这里插入图片描述
但是通过new可以显式的对他进行初始化

int* p2 = new int(5);

只需后面加上(),括号内给值即可。

class Date
{
private:
	int _year;
	int _month;
	int _day;
};
	Date* p3 = (Date*)malloc(sizeof(Date));
	Date* p4 = new Date;

	free(p3);
	delete p4;

对于自定义类型来说malloc只是开辟了一段Date这个对象大小的空间,并没有初始化,而通过new申请空间时它会自动调用这个对象的构造函数对它进行初始化,并且free也仅仅是释放这个对象的空间,如果成员变量中包含在堆上动态申请的空间例如栈,它并不会对栈进行释放,而delete会自动调用析构函数对成员变量中动态开辟的空间进行释放。
如下:

class Stack
{
public:
	Stack()
	{

	}

	~Stack()   
	{

	}
private: 
	int* _a;//成员变量需要在堆上动态申请空间
	int _capacity;
	int _size;
};

动态申请数组

	int* p5 = (int*)malloc(sizeof(int) * 5);
	int* p6 = new int[5];

	free(p5);
	delete[] p6;

对于内置类型的数组而言,在申请空间之后也不会对它进行初始化,但是通过new可以在申请数组的同时对它进行初始化

	int* p5 = (int*)malloc(sizeof(int) * 5);
	int* p6 = new int[5] {1, 4, 5};

在这里插入图片描述
当通过new申请数组时,如果给的初始值不够那么就会用0来初始化其它值。如果不给初始值那么就不会进行初始化。
对于释放通过new开辟的数组空间,要通过delete关键字加上[],再加上指针来完成空间的释放,并且会先调用析构函数。
重要:内置类型对象也有它对应的默认构造和析构函数,只不过用户无法自己去定义。比如我们在delete一个自定义类型对象时,它的成员变量中有一个动态开辟的int类型的数组,在delete时先调用的是这个对象的析构函数,对象的析构函数中调用的是内置类型的析构函数,释放动态申请的空间,两次析构函数不是同一个所以不会发生死循环。

函数模板

函数模板是什么?为什么要有函数模板,比如我们在对两个变量值进行交换时,这两个变量的类型可能为int,也可能为double等等,难道我们要把各种类型的Swap函数都要写一遍吗?只是把参数的类型换了一下,函数框架并没有改变,这样显然是繁琐的,因此C++中引入了模板,模板可以根据你传来的实参进行自动的推导。
模板关键字为:template

#include <iostream>

using namespace std;

//template<class T1, class T2>
template<typename T1, typename T2>
void Swap(T1& x, T2& y)
{
	T1 tmp = x;
	x = y;
	y = tmp;
}

int main()
{
	int a = 5;
	int b = 6;
	double c = 7.6;
	Swap(a, b);
	Swap(a, c);
}

这里的Swap只是一个模板,在我们调用时把实参传过去,模板会自动根据实参类型实例化出对应的函数

Swap(a, b);//实例化为void Swap(int& x, int& y)
Swap(a, c);//实例化为void Swap(int& x, double& y)

初学阶段可以认为class和typename没有区别,但是在C++中我们还是习惯用typename因为见名知意嘛,就是类型名
那么下面模板写成这样对不对?

template<typename T>
void Swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}

此时就取决于你传递的实参类型了,如果实参类型相同,那么没什么问题
如果实参类型不同比如Swap(a, c);

Swap(a, c);//实例化为void Swap(int& x, double& y)

对于函数体内的T而言它就会出现歧义,是int类型呢还是double类型呢?
对于这种情况有两种解决方法:

隐式实例化

template<typename T>
void Swap(const T& x, const T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}

int main()
{
	int a = 5;
	int b = 6;
	double c = 7.6;
	Swap(a, b);
	Swap(a, (int)c);
	Swap((double)a, c);
}

直接对实参进行强制类型转换,然后再让编译器根据实参推导出模板参数的实际类型。
注意我们这里模板中的形参前都加了一个const,这是为什么呢?
因为我们这里对实参进行了强制类型转换,实际上这里也发生了隐式类型转换,例如(int)c它有double类型转换成了int类型,意识类型转换的过程中又会产生临时变量,所以传过去的实际是实参的拷贝,而拷贝又具有常性,在引用前不加const就会导致权限放大,所以一般情况下我们在不改变形参的情况下尽量都要在前面加上const。

显式实例化

int main()
{
	int a = 5;
	int b = 6;
	double c = 7.6;
	Swap<double>(a, c);
	return 0;
}

在函数后的<>中指定模板参数的实际类型,这里指定模板参数类型为double
其实显式实例化通过下面的栈更能体现出它的作用,在栈中没有任何函数实参的类型能推断出T的类型,因此我们必须提供显式的模板实参
隐式实例化和显式实例化区别:
隐式实例化是传过去一个参数让编译器根据实参去推模板参数的实际类型,必须要有参数传过来才可确定模板实参类型。
显式实例化是直接指定模板参数的类型,可以没有参数传过来。

类模板

类模板同理,也是给只是类型不同的类抽象出来的模板,比如在数据结构栈中,在C语言阶段我们如果要去存不同类型的数据就要定义出多个不同类型的栈,但是在C++我们可以给定一个模板,然后让编译器这个老大哥帮我们去自动推导要存的数据类型。

#include <iostream>

using namespace std;

template<typename T>
class Stack
{
public:
	Stack(const int n);
	~Stack();
	void StackPush(T x);
	void StackPop();
	int StackSize();
private:
	T* _a;
	int _capacity;
	size_t _size;
};

template<typename T>
Stack<T>::Stack(const int n)//初始化列表是成员变量+括号,   不是加=等于号
	:
	_a(new T[n]),
	_capacity(n),
	_size(0)
{
	
}

//template<typename T>
//Stack<T>::Stack(const int n)
//{
//	T*  _a = new T[n];
//	int _capacity = n;
//	int _size = 0;
//}

template<typename T>
Stack<T>::~Stack()
{
	if (_a)
	{
		delete[] _a;
	}
	_capacity = _size = 0;
}

template<typename T>
void Stack<T>::StackPush(const T x)
{
	if (_size == _capacity)
	{
		perror("栈满了");
		return;
	}
	_a[_size] = x;
	_size++;
}

template<typename T>
void Stack<T>::StackPop()
{
	if (StackSize() <= 0)
	{
		cout << "栈中没有元素" << endl;
		return;
	}
	_size--;
}
template<typename T>
int Stack<T>::StackSize()
{
	return _size;
}

int main()
{
	Stack<int> S = 5;
	S.StackPush(1);
	S.StackPush(2);
	S.StackPush(3);
	S.StackPush(4);
	S.StackPush(5);
	S.StackPop();
	S.StackPop();
	S.StackPop();
	return 0;
}

1.在类模板中声明和定义可以分离,但是不建议分离到两个不同文件中,一般都放在头文件中
2.如果类模板中的成员函数声明和定义分离,那么在每个分离的函数定义之前我们就要再写一遍关键字 template+模板参数列表,因为编译器不知道这里的T是什么,也不知道去哪里找。
3.在普通类中类名就是类型名,比如之前的日期类Date,它的类型也是Date,但是在类模板中类模板名不等同于类型名,类型名是类名+,如果T已经确定为具体的类型比如int那么它对应的类型名就是类模板名+。
4.在对模板类进行实例化时,我们要指定T的类型,比如上面的栈如果我们要存储的类型是int类型,那么在对它进行实例化时对应的对象类型就是Stack
5.delete先调用的是int的析构函数释放数组空间,再free掉这个对象。所以这里在析构函数中使用delete不会出现死循环,因为两次调用的析构函数不是同一个。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

原工程运行正常,重新复制一份后再 npm install 后再运行就报错的解决办法

原工程&#xff0c;运行正常 将刚刚的工程复制一份呢&#xff0c;重新 npm install 再 npm run serve 就报错 出现这个问题十之八九都是依赖的问题。有可能是因为这个工程里面之前安装过一些东西&#xff0c;后来莫名其妙的就把 package.json 里面相关的依赖给删掉了。但由于原…

【数据结构】顺序二叉树的实现—以堆的实现为例、堆的调整、堆的创建、堆的插入和删除、堆排序

文章目录 1.堆的概念及结构2.堆的实现&#xff08;以大堆为例&#xff09;2.1堆的插入2.1.1堆的向上调整算法 2.2堆的删除2.2.1堆的向下调整算法 2.3堆的创建2.4有关建堆的时间复杂度 3.堆排序4.C语言堆实现源码 1.堆的概念及结构 堆就是顺序结构二叉树。 如果有一个关键码的集…

jetcache参考文档

jetcache简介 https://github.com/alibaba/jetcache/blob/master/docs/CN/GettingStarted.md 简介 JetCache是一个基于Java的缓存系统封装&#xff0c;提供统一的API和注解来简化缓存的使用。 JetCache提供了比SpringCache更加强大的注解&#xff0c;可以原生的支持TTL、两级…

shell变量类型含义 长方形面积计算 截取相应字段方法

1.总结变量的类型及含义&#xff1f; 2.实现课堂案例计算长方形面积&#xff1f;&#xff08;6种方式&#xff09; 3.定义变量urlhttps://blog.csdn.net/weixin_45029822/article/details/103568815&#xff08;通过多种方法实现&#xff09; 1&#xff09;截取网站访问的协议 …

文心一言 VS 讯飞星火 VS chatgpt (22)-- 算法导论4.2 2题

二、为Strassen算法编写伪代码。 文心一言&#xff1a; Strassen算法是一种用于矩阵乘法的算法&#xff0c;它的核心思想是通过不断将原始矩阵分割成较小的子矩阵来实现高效的乘法运算。以下是Strassen算法的伪代码&#xff1a; function Strassen(A, B): if A[0][0] 0 or…

Springboot整合WebSocket

目录 1.WebSocket 简介 2.WebSocket 实战 1.WebSocket 简介 WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单&#xff0c;允许服务端主动向客户端推送数据。在WebSocket API中&#xff0c;浏览器和服务器只需要…

基于html+css的图展示98

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

C++入门

文章目录 &#x1f600;1. 命名空间&#x1f604;1.1 命名空间定义&#x1f604;1.2 命名空间使用 &#x1f60a;2. 输入和输出&#x1f609;3. 缺省参数&#x1fae0;3.1 缺省参数概念&#x1fae0;3.2 缺省参数分类 &#x1f62c;4. 函数重载&#x1f644;4.1 函数重载概念&a…

TypeScript9-声明文件

本篇文章来讲 TypeScript 的声明文件。 当我们在使用第三方库的时候&#xff0c;很多第三方库不是用 TS 写的&#xff0c;它们是通过原生的 JavaScript 或者是浏览器 / 或者是 node 提供的 run time 对象。如果我们直接使用 TS 肯定就会报编译不通过。 1. 声明语句 假设一个…

浅析变电站无人值守管理的模式与特点

安科瑞虞佳豪 近年来&#xff0c;随着电网的发展&#xff0c;变电站实行无人值班管理模式已成为电网的发展方向。自1998年始&#xff0c;辉县市就开始了变电站综合自动化改造&#xff0c;截止目前全局所属24座变电站&#xff08;其中35kV19座、110kV5座&#xff09;已全部实现…

API自动化测试【postman生成报告】

PostMan生成测试报告有两种&#xff1a; 1、控制台的模式 2、HTML的测试报告 使用到一个工具newman Node.js是前端的一个组件&#xff0c;主要可以使用它来开发异步的程序。 一、控制台的模式 1、安装node.js 双击node.js进行安装&#xff0c;安装成功后在控制台输入node …

Java集合基础

4 集合基础 集合提供一种存储空间可变的存储模型,存储的数据容量可以改变ArrayLis<>: 可调整大小的数组实现<>:是一种特殊的数据类型,泛型可储存重复元素怎么使用呢 在出现E的地方我们使用引用数据类型替换即可举例:ArrayList<String>、ArrayList<Stu…

财务共享时代企业数智化应用能帮我们做些什么?

随着企业规模的不断扩大和业务范围的逐步扩展&#xff0c;财务工作的难度和复杂度也在不断提高&#xff0c;传统的手工录入和处理方式呈现出流程长、效率低、易出错等问题。为了提升财务工作的效率和准确性&#xff0c;越来越多的企业开始利用数智化应用打造企业内部的财务数智…

绝对不能错过的7个零基础免费的ChatGPT镜像网站

还在为打不开openai官网烦心&#xff1f;本文帮你实现ChatGPTMidJourney自由(&#xffe3;∇&#xffe3;)/ &#x1f4d2;收集了一些截至目前(2023年5月25日午12:00)可以免费访问&#xff0c;并且零基础也能正常使用的镜像网站&#xff0c;后续将持续维护更新(&#xff61;&a…

平安银行广州分行立足地域文化,增强差异化权益服务软实力

立足地域文化&#xff0c;拓展差异化权益服务 瓦屋纸窗之下&#xff0c;一盏清茶&#xff0c;三五好友&#xff0c;怡然自若。中国人对茶的喜爱由来已久&#xff0c;茶文化已成为中华传统文化中一张亮丽的名片&#xff0c;而广东茶文化则是中国四大茶文化系列之一。平安银行广州…

抖音seo优化源代码搭建+抖音小程序私有化开源部署

抖音seo优化源码&#xff0c;抖音seo矩阵系统搭建&#xff0c;抖音账号矩阵系统开发&#xff0c;企业在做账号矩阵过程中&#xff0c;最头疼的莫过于私域线索转化&#xff0c;作为开发者都知道&#xff0c;目前市面上我们了解的矩阵系统除了挂载POI信息外&#xff0c;无法挂载留…

unity四叉树和视锥体剔除

这个最好还是看代码&#xff0c;项目有注释放在这里&#xff1a; GetbadEarlyup/Quadtree-cone-scene: 这是一个unity四叉树场景视锥体剔除的Demo (github.com)https://github.com/GetbadEarlyup/Quadtree-cone-scene国内地址&#xff1a; Quadtree-cone-scene: unity四叉树和…

VESD静电监控系统:提高静电防护效果与管理效率

随着科学技术不断发展&#xff0c;现代的工业对静电防护的要求越来越高。因为静电的存在可能会对产品质量、工作环境、甚至是人身产生威胁。静电监控系统是一项高效的管理工具&#xff0c;能够有效地控制和监测静电产生的情况&#xff0c;提高静电防护效果和管理效率。 VESD静电…

U盘超级加密3000试用版与正式版的区别有哪些?

U盘超级加密3000是一款专业的U盘加密软件&#xff0c;它可以为U盘、移动硬盘、内存卡等移动存储设备加密。软件拥有正式版和试用版&#xff0c;那么这两个版本有什么区别呢&#xff1f;下面我们就一起来了解一下。 U盘超级加密3000试用版和正式版的区别 打开软件时的区别 试用…

基于SSM的酒店客房管理系统

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 酒店管理系统是一款高…