C++内存管理和模板

news/2024/11/25 9:29:39/

文章目录

  • 内存管理
    • 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不会出现死循环,因为两次调用的析构函数不是同一个。


http://www.ppmy.cn/news/83522.html

相关文章

Handler、Looper、Message 和 Thread 的合作机制——安卓 Handler 机制、跨线程机制详解

我们都知道平时在子线程要刷新UI线程的时候一般会用 runOnUiThread(new Runnable() {Overridepublic void run() {} }); 或者 handler.post(new Runnable(){ }); 那么Handler机制是怎么实现跨线程通信的呢&#xff1f;提到Handler其实他就像是一个机制对外开放的API,实际的核…

【PC迁移与管理】上海道宁为每个用户和每个 PC 传输和迁移场景提供解决方案——PCmover

PCmover 是一款 可以自动将所有选定文件、 文件夹、设置、用户配置文件 甚至应用程序 从旧PC传输、恢复和升级到 新PC或操作系统的软件 而且由于 大多数迁移的应用程序 都已安装在新PC上即可使用 通常无需查找旧CD 以前下载的程序 序列号或许可证代码 开发商介绍 La…

「实在RPA·银行数字员工」切实助力银行业务提质增效合规

在数字经济浪潮下&#xff0c;金融科技的发展和应用给传统银行机构带来的“创造性威胁”逐渐显现&#xff0c;加快数字经济建设&#xff0c;全面推进银行业数字化转型&#xff0c;推动金融高质量发展&#xff0c;更好服务实体经济和满足人民群众需要&#xff0c;对银行机构稳健…

16:00面试,还没10分钟就出来了 ,问的实在是太...

自从加入这家公司&#xff0c;每天都在加班&#xff0c;钱倒是给的不少&#xff0c;所以也就忍了。没想到8月一纸通知&#xff0c;所有人不许加班&#xff0c;薪资直降30%&#xff0c;顿时有吃不起饭的赶脚。 好在有个兄弟内推我去了一家互联网公司&#xff0c;兴冲冲见面试官…

如何发起一次完整的HTTP的请求流程

目录 &#x1f4a1; 预备知识 &#x1f50a; 浏览器端发起 HTTP 请求流程 1、构建请求 2、查找缓存 3、准备IP地址和端口 4、等待TCP队列 5、建立TCP连接 6、发送HTTP请求 &#x1f50a; 服务器端处理 HTTP 请求流程 1、返回请求 2、断开连接 3、重定向 HTTP 是一种…

如何减少DevOps工具的泛滥

在过去十年投资于devops之后&#xff0c;许多公司正在经历某种后遗症&#xff1a;工具蔓延。虽然他们的软件交付流程变得更加精简、高效和可靠&#xff0c;但他们也拥有更多的工具来许可、维护和管理。 工具蔓延通常被视为开发团队的灵活性和授权选择他们自己的工具的自然结果…

【论文速递】ICLR2022 - 语言驱动的语义分割

【论文速递】ICLR2022 - 语言驱动的语义分割 【论文原文】&#xff1a;LANGUAGE-DRIVEN SEMANTIC SEGMENTATION 【作者信息】&#xff1a;Boyi Li Cornell University, Cornell Tech Kilian Q. Weinberger Cornell University Serge Belongie University of Copenhagen Vladl…

HTML-iconfont动态图标SVG效果--阿里巴巴图标矢量库

给北大打工&#xff0c;实现官网首页动态图标效果_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Ys4y1c7oh/?spm_id_from333.1007.top_right_bar_window_default_collection.content.click&vd_source924f5dad6f2dcb0a3e5dca4604287ecd&#xff08;本篇笔记操作方法…