C++——模板初阶

泛型编程

C语言中交换两个变量数据的内容一般是这样实现的

#include<iostream>

using namespace std;

void swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}
int main()
{
	int x = 5;
	int y = 7;

    swap(&x,&y);
   cout << "x=" << x << " ";
   cout << "y=" << y << " ";

}

但自从学了C++引用以后 就不用再传地址和用指针接受实参地址了,直接使用引用 既高效又不开辟栈帧空间。

#include<iostream>

using namespace std;

void swap(int &xx, int&yy)
{
	int tmp = xx;
	xx = yy;
	yy = tmp;
}
int main()
{
	int x = 5;
	int y = 7;

    swap(x,y);
   cout << "x=" << x << " ";
   cout << "y=" << y << " ";

}

结果是一样的。 

但如果要实现多种不同类型数据之间的交换呢?

C++可以使用函数重载

void Swap(int& left, int& right)
{
 int temp = left;
 left = right;
 right = temp;
}
void Swap(double& left, double& right)
{
 double temp = left;
 left = right;
 right = temp;
}
void Swap(char& left, char& right)
{
 char temp = left;
 left = right;
 right = temp;
}

虽然函数重载可以实现 但有几个不好的地方

1.重载的函数仅仅是 类型不同,代码复用率比较低,如果产生了新的类型,就需要自己添加对应类型函数重载。
2.代码的维护性比较低,一个出错可能所有重载均可出错。
那有什么好的解决办法呢?
我们是否可以告诉编译器一个模子,让编译器自己根据不同的类型利用模子生成所对应代码呢?
事实上是可以的,因为C++的祖师爷已经考虑到了这点,并且已经实现了出来。
正所谓前人栽树 后人乘凉 
一般是 泛型编程
泛型编程编写与类型无关的通用代码,是代码复用的一种手段,模板是泛型编程的基础。

模板

模板分为 函数模板类模板

函数模板

函数模板是怎么实现两个数据之间的交换呢?
#include<iostream>

using namespace std;

template<class T>
void swap(const T&x, const T&y)
{
	T tmp = x;
	x = y;
	y = x;
}
int main()
{
	int x = 5;
	int y = 7;

    swap(x,y);
   cout << "x=" << x << " ";
   cout << "y=" << y << " ";

}

函数模板的格式

template<typename T1,typename T2,.......typename Tn>

返回值类型 函数名(参数)

typename是用来定义模板参数的关键字,也可以用class。(一般是用class,但不能以struct替代class)

#include<iostream>

using namespace std;

template<class T>
void swap(const T&x, const T&y)
{
	T tmp = x;
	x = y;
	y = x;
}
int main()
{
	int x = 5;
	int y = 7;
	double a = 1.34;
	double b = 2.34;
    swap(x,y);
	swap(a, b);
   cout << "x=" << x << endl;
   cout << "y=" << y << endl;
   cout << "a=" << a << endl;
   cout << "b=" << b << endl;

}

模板会根据你传的实参类型自动推演生成对应数据类型的函数。 

但它们俩调用的却不是同一个函数 通过反汇编就可以看出来

编译器自动完成的事情。

函数模板的原理

要清楚模板是在编译阶段就调用完成的。

当数据类型使用函数模板时,编译器通过对实参类型的推演,将T确定为与之对应的类型,然后专门生成处理该数据类型的代码。
当然模板里面的定义的关键字类型也可以 有多种类型
#include<iostream>

using namespace std;

template<class T,class Y>
T add(const T& x, const Y& y)
{
	return x + y;
}


int main()
{
	int a = 4;
	double b = 3.14;
	int ret=add(a, b);
	cout << ret << endl;
}

函数模板的实例化

模板参数语法其实很类似函数参数,函数参数定义的形参对象,模板参数定义的是类型 

用不同类型的参数使用函数模板时,是函数模板的实例化。

而实例化又分为隐式实例化显示实例化

隐式实例化:让编译器根据实参类型推演模板参数实际类型的过程

显式实例化:  在函数名后的<>中指定模板参数的实际类型

#include<iostream>

using namespace std;

template<class T>
T Add(const T& x, const T& y)
{
	return x + y;
}


int main()
{
	int a = 5;
	int b = 11;
	double c = 3.14;
	double d = 4.14;
	int ret = Add(a, b);
	double ret1 = Add(c, d);

	int ret2 =Add(a, d);error
	该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
	通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T,
	编译器无法确定此处到底该将T确定为int 或者 double类型而报错
	注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅
    所以为了解决这个问题 1.要么用户自己强转 2.要么显式实例化
	int ret3 = Add(a, (int)d);

	cout << ret << endl;
	cout << ret1 << endl;
	cout << ret3 << endl;
    显式实例化
	cout << Add<int>(a, c) << endl;
	cout << Add<double>(a, b) << endl;
}

 一般这种时候就要显式实例化

否则编译器也不知道f是返回值是什么?

 模板参数的匹配原则

一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数

#include<iostream>

using namespace std;

普通函数
int Add(const int x, const int y)
{
	cout << "(const int x, const int y)" << endl;
	return x + y;
}

函数模板
template<class T>
T Add(const T& x, const T& y)
{
	cout << "(const T& x, const T& y)" << endl;
	return x + y;
}

int main()
{
	Add(1, 2);
	Add(1.1,1.2);
}

当我把函数模板屏蔽后再运行

double到int可以进行隐式类型转换 所以去调用了普通函数。

由此我们可以发现模板参数匹配调用原则:

1.有现成的吃现成的   (第一个int类型去调用普通函数 第二个double类型去调用函数模板)

2.有适合的吃适合的,哪怕自己要现做 

3.没有就将就吃 (函数模板屏蔽后,去调用普通函数)

类模板

类模板的格式

template<class T1,class T2class Tn>

class 类模板名

{
// 类内成员定义

}

#include<iostream>

using namespace std;

template<class T>

class stack
{
public:n
	stack(int n = 4)
	{
		cout << "stack(int n = 4)" << endl;
		_a = new T[n];
		_top = 0;
		_capacity = n;
	}
	~stack()
	{
		cout << "~stack()" << endl;
		delete _a;
		_a = nullptr;
		_top = _capacity = 0;
	}

private:
	T* _a;
	int _top;
	int _capacity;
};


如果想声明和定义分离这样写
但模板一般声明和定义都是在一个文件写的 不会分开写 因为会产生链接错误
暂且先不讲 我在模板进阶再详细说
template<class T>
Stack<T>::Stack(int n)
{
	cout << "Stack(int n = 4)" << endl;

	_a = new T[n];
	_top = 0;
	_capacity = n;
}

int main()
{
	显式实例化
	stack<int>st;
	stack<double>st;
}

拿栈来做例子,C语言里面如果栈里面要存int和double类型数据 还要typedef 成intdatatype 或者doubledatatype 还要把类型名改成stackint 或者stackdouble

而有了类模板以后就避免了这种低效用法,直接显式实例化要存的类型数据,类模板参数直接推演出对应存储数据类型。

类模板的实例化

类模板实例化函数模板实例化不同类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。

#include<iostream>
#include<vector>
#include<stack>
using namespace std;

int main()
{
   vector是类名  vector<int>才是类型
   vector<int>s;

   同理stack是类名  stack<double>才是类型
   stack<double>st;
 
     

    return 0;
}

总结:普通类----类名是类型   类模板----类名<数据类型>才是整个类的类型。

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

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

相关文章

基于栈结构的非递归二叉树结点关键字输出算法

基于栈结构的非递归二叉树结点关键字输出算法 一、引言二、二叉树基本概念三、非递归遍历算法基础四、算法设计五、算法实现六、C代码示例七、算法分析八、优化与讨论 一、引言 在计算机科学中&#xff0c;二叉树是一种重要的数据结构&#xff0c;它广泛应用于各种算法和数据结…

基于深度学习的条形码二维码检测系统(网页版+YOLOv8/v7/v6/v5代码+训练数据集)

摘要&#xff1a;本文深入研究了基于YOLOv8/v7/v6/v5的条形码二维码检测系统。核心采用YOLOv8并整合了YOLOv7、YOLOv6、YOLOv5算法&#xff0c;进行性能指标对比&#xff1b;详述了国内外研究现状、数据集处理、算法原理、模型构建与训练代码&#xff0c;及基于Streamlit的交互…

前端优化gzip

gzip是GNUzip的缩写&#xff0c;是一种文件的压缩格式&#xff08;也可以说是若干种文件压缩程序&#xff09;&#xff0c;类似的压缩格式还有compress&#xff08;webpack&#xff09;&#xff0c;deflate等 主要用于组件的压缩 压缩的话主要分为两种&#xff0c; 服务器在…

记事本打开时总是会自动打开之前打开过的文件

记事本打开文件总是会自动打开之前打开过的文件_win11记事本关闭之后打开上一个还在-CSDN博客 感谢该博主&#xff0c;我一直以为是自己电脑的问题&#xff0c;不知道为什么要这么更新&#xff0c;影响我的很多文本内容消失。

我的领导马斯克:痛恨开会,不要非技术中层,推崇裁员

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了免费的人工智能中文站https://ai.weoknow.com 新建了收费的人工智能中文站https://ai.hzytsoft.cn/ 更多资源欢迎关注 马斯克称得上是个“魔鬼老板”这事儿&#xff0c;已经出了名了。 现在&#xff0c;他的老部…

【面试八股总结】进程(一)

参考资料 &#xff1a;小林Coding、阿秀、代码随想录 一、什么是进程&#xff1f; 1. 基本概念 进程是具有独立功能的程序在一个数据集合上运行的过程&#xff0c;是系统进行资源分配和调度的一个独立单位。 2. 进程控制块 系统通过进程控制块PCB描述进程的进本情况…

leetcode代码记录(打家劫舍 II

目录 1. 题目&#xff1a;2. 我的代码&#xff1a;小结&#xff1a; 1. 题目&#xff1a; 一个专业的小偷&#xff0c;计划偷窃一个环形街道上沿街的房屋&#xff0c;每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 &#xff0c;这意味着第一个房屋和最后一个房屋是…

基于小程序实现的校园二手物品交易系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;spring…

openlayers 入门教程(九):overlay 篇

还是大剑师兰特&#xff1a;曾是美国某知名大学计算机专业研究生&#xff0c;现为航空航海领域高级前端工程师&#xff1b;CSDN知名博主&#xff0c;GIS领域优质创作者&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;canvas&#xff0c;webgl&#xff0c;ech…

vulhub中Apache Solr RemoteStreaming 文件读取与SSRF漏洞复现

Apache Solr 是一个开源的搜索服务器。在Apache Solr未开启认证的情况下&#xff0c;攻击者可直接构造特定请求开启特定配置&#xff0c;并最终造成SSRF或任意文件读取。 访问http://your-ip:8983即可查看Apache Solr后台 1.访问http://your-ip:8983/solr/admin/cores?indexI…

Windows10安装CloudCompare(图文安装)

CloudCompare是一个3D点云&#xff08;和三角网格&#xff09;处理软件。它最初被设计用于在两个密集的3D点云&#xff08;例如用激光扫描仪获取的点云&#xff09;之间或点云和三角形网格之间进行比较。它依赖于专用于此任务的特定八叉树结构。 之后&#xff0c;它已经扩展到一…

使用 CloudDM 操作 PostgrgSQL 数据库

CloudDM 简介 CloudDM 是 ClouGence 公司推出的一款一站式数据库管理工具&#xff0c;使用它可以方便地访问和管理 MySQL、Oracle、PostgreSQL、阿里云 RDS、Greenplum、TiDB、Redis、StarRocks、Doris、SelectDB、SQL SERVER、ClickHouse、OceanBase 、PolarDB-X 、IBM Db2 等…

LearnOpenGL_part1

创建窗口 - LearnOpenGL CN (learnopengl-cn.github.io) 最原始的黑框框&#xff1a; #include <glad/glad.h> #include <GLFW/glfw3.h> #include <iostream> int main() {glfwInit();//初始化GLFWglfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//配置G…

【JavaScript 漫游】【052】Proxy

文章简介 本篇文章为【JavaScript 漫游】专栏的第 052 篇文章&#xff0c;记录了 ES6 规范中 Proxy 的知识点。 概述 Proxy 用于修改某些操作的默认行为&#xff0c;等同于在语言层面做出修改&#xff0c;所以属于一种“元编程”&#xff08;meta programming&#xff09;&a…

C/C++程序的(编译,链接)翻译与运行

目录 前言&#xff1a; 1.程序环境 2.翻译环境 3.预处理&#xff08;预编译&#xff09; 4.编译 5.汇编 6.链接 7.运行环境 总结&#xff1a; 前言&#xff1a; 本篇来解释c/c程序的翻译环境与运行环境中的过程&#xff0c;不同的编程语言的翻译环境类似&#xff0c;…

LeetCode-114. 二叉树展开为链表【栈 树 深度优先搜索 链表 二叉树】

LeetCode-114. 二叉树展开为链表【栈 树 深度优先搜索 链表 二叉树】 题目描述&#xff1a;解题思路一&#xff1a;前序遍历&#xff0c;迭代&#xff0c;递归解题思路二&#xff1a;寻找前驱节点解题思路三&#xff1a;0 题目描述&#xff1a; 给你二叉树的根结点 root &…

scoped原理及使用

一、什么是scoped&#xff0c;为什么要用 在vue文件中的style标签上&#xff0c;有一个特殊的属性&#xff1a;scoped。 当一个style标签拥有scoped属性时&#xff0c;它的CSS样式就只能作用于当前的组件&#xff0c;通过该属性&#xff0c;可以使得组件之间的样式不互相污染。…

synchronized到底锁住的是谁?

我们使用synchronized关键字是用来实现线程同步的&#xff0c;当多个线程同时去争抢同一个资源的时候在资源上边加一个synchronized关键字&#xff0c;能够使得线程排队去完成操作。 synchronized到底锁定的是什么资源&#xff1f; 修饰方法非静态方法 &#xff0c;锁定的是方…

LeetCode 1379.找出克隆二叉树中的相同节点:二叉树遍历

【LetMeFly】1379.找出克隆二叉树中的相同节点&#xff1a;二叉树遍历 力扣题目链接&#xff1a;https://leetcode.cn/problems/find-a-corresponding-node-of-a-binary-tree-in-a-clone-of-that-tree/ 给你两棵二叉树&#xff0c;原始树 original 和克隆树 cloned&#xff0…

SpringMvc工作流程

用户通过浏览器发送请求到前端控制器DispatcherServlet。前端控制器直接将请求转给处理器映射器HandlerMapping。处理器映射器HandlerMapping会根据请求&#xff0c;找到负责处理该请求的处理器&#xff0c;并将其封装为处理器执行链HandlerExecutionChina后返回给前端控制器Di…