IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【C++】模板(函数模板/类模板/template/typename) -> 正文阅读

[C++知识库]【C++】模板(函数模板/类模板/template/typename)

C++模板

函数模板

我们看一段代码,我们要实现两个int的交换函数,可以这么实现

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

但是,我们想实现两个double或者char类型的加法,就得把这个函数再写两次

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;
}

我们可以看出,这几个函数只是参数不同,内部的实现是完全相同的,这么写的话,我们的代码会很冗余,代码的复用性不高,为了解决这个问题,C++引入了函数模板来解决这个问题

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。

函数模板的格式:

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

返回值类型 函数名(参数列表){}

上面的交换函数就可以写为函数模板的形式,增强代码的复用性:

//其中typename也可以写成class,T就是被我们参数化的变量。
template<typename T>
void Swap(T& left, T& right) {
	T tem = left;
	left = right;
	right = tem;
}
//上面的一块是一个整体统称函数模板
int main() {
	int a1 = 88;
	int b1 = 66;
	char a2 = 'A';
	char b2 = 'B';
    //不同的变量参数经过函数模板的转化,就能生成不同的函数,实现不同类型的交换函数
	Swap(a1, b1);
	Swap(a2, b2);
	return 0;
}

我们用函数模板编写一个不同类型的加法函数,我们可以把函数内所有可以用到类型的地方都用函数模板来传递参数。

template<typename T>
//返回值也可以用传入的参数T来代替
T add(T a, T b) {
	return a + b;
}
int main() {
    //自动推导参数类型并传参到T
	add(55, 55);
	return 0;
}

这种写法是隐式传递,T的类型是通过我们传入的55值推导出来的,我们也可以显式传递

template<typename T,typename T1, typename T2>
T add(T1 a,T2 b) {
	return a + b;
}
int main() {
    //显式传参的参数我们写到函数后面的<>内,参数可以传递多个,顺序对应上面的类型顺序
	add<int, char, int>('A', 66);
	add<int, double, int>(55.55, 66);
	return 0;
}

这样的写法看似很简单,我们写起来也更加的方便,传入不同的参数,都是通过这个函数模板来运行,似乎效率也更高了。

但是其实和我们写多个函数并调用做的事情是差不多的

函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器

在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。

所以说使用函数模板并不能提升代码的效率,只是让程序员写代码更加方便,后续的管理也更方便。

int add(int a, int b) {
	return a + b;
}
template<typename T>
T add(T a, T b) {
	return a + b;
}
int main() {
    //调用具体函数
	add(55, 33);
    //只能调用函数模板
	add<int>(66, 88);
	return 0;
}

由于函数模板并没有提升效率,而且传入参数以后也要生成代码以后才能调用,所以我们函数模板和具体的函数同时存在时,会优先匹配具体的函数。

但是我们要是显式传递了int参数,就只能匹配函数模板生成代码后再调用。


类模板

类模板的用法和函数模板类似,也是在类的前面写上**template<class T1, class T2,…,class Tn>**把类实现为一个模板类。然后把需要的成员变量用参数传。

我们实现一个顺序表,其中的数据成员类型我们就可以用Type传入,实现由一个模板类完成不同数据成员的顺序表

template <class Type>
class SeqList {
public:
	SeqList(Type sz) 
		:capacity(sz)
		,base(new Type[capacity])
		,size(0)
	{}
	void Push_Back(const Type& data) {
		base[size++]=data;
	}
	void show() {
		for (int i = 0; i < size; ++i) {
			cout << base[i] << " ";
		}
		cout << endl;
	}
	~SeqList() {
		delete[] base;
		base = nullptr;
		capacity = size = 0;
	}
private:
	Type* base;
	size_t capacity;
	size_t size;
};

/
int main() {
	SeqList<int> lt1(10);
	for (int i = 1; i <= 10; ++i) {
		lt1.Push_Back(i);
	}
	lt1.show();
	return 0;
}

要注意的是,要想把模板类的函数实现在类外,写法得像下面这样写

template <class Type>
class SeqList {
public:
	SeqList(Type sz) 
		:capacity(sz)
		,base(new Type[capacity])
		,size(0)
	{}
    //类内声明
	void Push_Back(const Type& data);
	void show();
	~SeqList() {
		delete[] base;
		base = nullptr;
		capacity = size = 0;
	}
private:
	Type* base;
	size_t capacity;
	size_t size;
};
//
//类外实现的时候,要注意每个函数都要实现为模板函数,在写类作用域限定符的时候要写出完整的模板参数列表
template <class Type>
void SeqList<Type>::Push_Back(const Type& data){
	base[size++] = data;
}
template <class Type>
void SeqList<Type>::show() {
	for (int i = 0; i < size; ++i) {
		cout << base[i] << " ";
	}
	cout << endl;
}
//
int main() {
	SeqList<int> lt1(10);
	for (int i = 1; i <= 10; ++i) {
		lt1.Push_Back(i);
	}
	lt1.show();
	return 0;
}

只要代码中有模板类或者模板函数,那么这份代码就不支持分离编译,也就是类和函数的声明和实现只能在一个文件中完成,不能头文件声明,源文件实现,如果分开编译,链接时就会发生链接错误。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-11-28 11:05:10  更:2021-11-28 11:06:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 10:03:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码