C++复习笔记——泛型编程模板

01 模板

模板就是建立通用的模具,大大提高复用性;

02 函数模板

C++另一种编程思想称为 泛型编程 ,主要利用的技术就是模板 C++
提供两种模板机制:函数模板和类模板

函数模板语法

函数模板作用: 建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。

template<typename T>   <=>  template<class T>
函数声明或定义(同一个函数不能用多个函数模板)
//可以一次建立多个通用数据类型
template<typename T, typename T2>
void Insert_Sort_T(T* array, T2 size)

解释:
template — 声明创建模板
typename — 表面其后面的符号是一种数据类型,可以用class代替
T — 通用的数据类型,名称可以替换,通常为大写字母

总结:

  1. 函数模板利用关键字 template
  2. 使用函数模板有两种方式:自动类型推导、显示指定类型
int a = 10;
int b = 20;
//利用模板实现交换
//1、自动类型推导
mySwap(a, b);
//2、显示指定类型
mySwap<int>(a, b);
  1. 模板的目的是为了提高复用性,将类型参数化
  2. 使用模板时必须确定出通用数据类型T,并且能够推导出一致的类型
    函数模板在自动推导时不可以进行隐式类型转换
//利用模板提供通用的交换函数
template<class T>
void mySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
// 1、自动类型推导,必须推导出一致的数据类型T,才可以使用
void test01()
{
int a = 10;
int b = 20;
char c = 'c';
mySwap(a, b); // 正确,可以推导出一致的T
//mySwap(a, c); // 错误,推导不出一致的T类型, 同时表明函数模板不可以进行隐式类型转换
}
// 2、模板必须要确定出T的数据类型,才可以使用
template<class T>
void func()
{
cout << "func 调用" << endl;
}
void test02()
{
//func(); //错误,模板不能独立使用,必须确定出T的类型
func<int>(); //利用显示指定类型的方式,给T一个类型,才可以使用该模板
}
int main() {
test01();
test02();
system("pause");
return 0;
}

函数模板与普通函数的区别以及调用规则

普通函数与函数模板区别:

  1. 普通函数调用时可以发生自动类型转换(隐式类型转换)
  2. 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换;如果利用显示指定类型的方式,可以发生隐式类型转换
//1 普通函数与函数模板区别
template<class T>
T myPlus(T a, T b)
{
	return a + b;
}
int myPlus2(int a, int b)
{
	return a + b;
}
void test01()
{
	int a = 10;
	int b = 20;
	char c = 'c'; // a = 97 
//	myPlus(a, c); //类型推导不出来 ,函数模板不可以进行隐式类型转换
	cout << myPlus<int>(a, c) << endl;//显式指定类型时 ,函数模板可以进行隐式类型转换
	cout << myPlus2(a, c) <<endl; // 10 + 99  普通函数 可以进行隐式类型转换
}

结果:
在这里插入图片描述

普通函数和函数模板的调用规则

  1. 如果出现重载 优先使用普通函数调用,如果普通函数没有实现只声明时仍会优先使用普通函数调用,导致出现错误:无法解析
  2. 如果在普通函数和函数模板出现重载时,想强制调用模板 ,那么可以使用空参数列表
myPrint<>(a, b)
  1. 函数模板之间可以发生重载
template<class T> 
void myPrint(T a ,T b) 
template<class T> 
void myPrint(T a, T b ,T c)
  1. 如果在普通函数和函数模板出现重载时,若函数模板可以产生更好的匹配,那么优先调用函数模板
void myPrint(int a, int  b)
{
	cout << "普通函数调用 myPrint" << endl;
}
template<class T>
void myPrint(T a ,T b)
{
	cout << "模板调用的myPrint" << endl;
}

代码:

//2 、普通函数和函数模板的调用规则
template<class T>
void myPrint(T a ,T b)
{
	cout << "模板调用的myPrint" << endl;
}

template<class T>
void myPrint(T a, T b ,T c)
{
	cout << "模板调用的myPrint(a,b,c)" << endl;
}
void myPrint(int a, int  b)
{
	cout << "普通函数调用 myPrint" << endl;
}
void test02()
{
	int a = 10;
	int b = 20;
	//1 、如果出现重载  优先使用普通函数调用,如果没有实现,出现错误
	myPrint(a, b); //myPrint(int a, int  b)只声明未实现时仍会优先使用普通函数调用,导致出现错误:无法解析
	//2、 如果想强制调用模板 ,那么可以使用空参数列表
	myPrint<>(a, b);
	//3、 函数模板可以发生重载
	int c = 30;
	myPrint(a, b, c);
	//4、 如果函数模板可以产生更好的匹配,那么优先调用函数模板
	char c1 = 'c';
	char d = 'd';
	myPrint(c1, d);
}

结果:
在这里插入图片描述

模板的局限性以及解决

局限性
模板的通用性并不是万能的 。对于某些特殊类型(数组、自定义类型)在函数模板内不能使用一些通用操作,导致出错

//数组
void f(T a, T b)
{
	a = b;
}
void test00() {
	int a1[1], a2[1];
	//a1 = a2;
	f(a1, a2);//未报错,传进去的是指针,避开了数组名是常量的限制
}
//自定义类型
class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;
};
template<class T>
bool myCompare( T &a , T &b )
{
	if ( a == b)
	{
		return true;
	}
	return false;
}
test:
	Person p1("Tom", 10);
	Person p2("Jerry", 10);
	int ret2 = myCompare(p1, p2);

结果:==运算符不能操作Person
在这里插入图片描述
解决办法: 具体化
提供模板的重载,可以为这些特定的类型提供具体化的模板,如果具体化能够优先匹配,那么就选择具体化
具体化语法:
template<> 返回值 函数名<具体类型>(参数)

template<> bool myCompare(Person&a, Person &b)

/*通过第三代具体化自定义数据类型,解决上述问题
 如果具体化能够优先匹配,那么就选择具体化
 语法  template<> 返回值  函数名<具体类型>(参数)*/ 
template<> bool myCompare<Person>(Person &a, Person &b)
{
	if ( a.m_Age  == b.m_Age)
	{
		return true;
	}
	return false;
}

总结:

● 利用具体化的模板,可以解决自定义类型的通用化
● 学习模板并不是为了写模板,而是在STL能够运用系统提供的模板

03 类模板

类模板语法

//类声明或定义
template<typename T1, typename T2>

解释:

template — 声明创建模板
typename — 表面其后面的符号是一种数据类型,可以用class代替
T — 通用的数据类型,名称可以替换,通常为大写字母

类模板与函数模板区别:

  1. 类模板可以有默认类型
    template <class NameType, class AgeType = int> //类模板可以有默认类型
  2. 类模板必须显示指定类型使用,无法自动类型推导
//自动类型推导 ,类模板 不支持
	//Person p("孙悟空", 100);
	//显示指定类型
	Person<string, int> p("孙悟空", 100);

类模板中成员函数创建时机
类模板中成员函数和普通类中成员函数创建时机是有区别的:

● 普通类中的成员函数一开始就可以创建
● 类模板中的成员函数在调用时才创建

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include <string>
using namespace std;
//类模板
template <class NameType, class AgeType = int> //类模板可以有默认类型
class Person
{
public:
class Person1
{
public:
	void showPerson1()
	{
		cout << "Person1的调用" << endl;
	}
};
class Person2
{
public:
	void showPerson2()
	{
		cout << "Person2的调用" << endl;
	}
};
template<class T>
class myClass
{
public:
	T obj;
	void func1()
	{
		//类模板中成员函数一开始不会创建出来,而是在运行时才去创建
		//所以可以通过编译不报错
		obj.showPerson1();
	}
	void func2()
	{
		obj.showPerson2();//obj未确定,但可以通过编译不报错
	}
};
//类模板中成员函数 一开始不会创建出来,而是在运行时才去创建
void test02()
{
	myClass<Person1>m;
	m.func1();
	m.func2();//**可以通过编译不报错,但运行时报错:"showPerson2": 不是 "Person1" 的成员**
}

总结:
类模板和函数模板语法相似,在声明模板template后面加类,此类称为类模板

类模板对象做函数参数

类模板实例化出的对象,向函数传参的方式 一共有三种传入方式:

  1. 指定传入的类型 — 直接显示对象的数据类型
void doWork( Person<string ,int> & p ) 
  1. 参数模板化 — 将对象中的参数变为模板进行传递
template<class T1 ,class T2>
void doWork2(Person<T1, T2> & p)
  1. 整个类模板化 — 将这个对象类型 模板化进行传递
template<class T>
void doWork3(T&p)
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include <string>
using namespace std;

//类模板
template <class NameType, class AgeType = int> //类模板可以有默认类型
class Person
{
public:
	Person(NameType name, AgeType age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	void showPerson()
	{
		cout << "姓名:" << this->m_Name << " 年龄: " << this->m_Age << endl;
	}
	NameType m_Name;
	AgeType m_Age;
};
//1  指定传入类型
void doWork( Person<string ,int> & p ) 
{
	p.showPerson();
}
void test01()
{
	Person <string, int> p("MT",10);
	doWork(p);
}
//2 参数模板化
template<class T1 ,class T2>
void doWork2(Person<T1, T2> & p)
{
	//如何查看类型
	cout << typeid(T1).name() << endl;
	cout << typeid(T2).name() << endl;
	p.showPerson();
}
void test02()
{
	Person <string, int> p("呆贼", 18);
	doWork2(p);
}
//3 整体模板化
template<class T>
void doWork3(T&p)
{
	cout << typeid(T).name() << endl;
	p.showPerson();
}
void test03()
{
	Person <string, int> p("劣人", 18);
	doWork3(p);
}
int main(){
//	test01();
	test02();
	test03();
	system("pause");
	return EXIT_SUCCESS;
}

类模板与继承

当类模板碰到继承时,需要注意一下几点:

  1. 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型。如果不指定,编译器无法给子类分配内存
//class Son:public Base //错误,c++编译需要给子类分配内存,必须知道父类中T的类型才可以向下继承
class Son :public Base<int> //必须指定一个类型
  1. 如果想灵活指定出父类中T的类型,子类也需变为类模板
    //类模板继承类模板 ,可以用T2指定父类中的T类型
    template<class T1, class T2>
    class Son2 :public Base
    总结:
    如果父类是类模板,子类需要指定出父类中T的数据类型。若想灵活指定父类中T的数据类型,子类也要变成类模板。

类模板实现注意事项

类模板成员函数类外实现
类模板中成员函数类外实现时,需要加上模板参数列表
#include
//类模板中成员函数类外实现

template<class T1, class T2>
class Person {
public:
//成员函数类内声明
Person(T1 name, T2 age);
void showPerson();
public:
T1 m_Name;
T2 m_Age;
};
//构造函数 类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}
//成员函数 类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}
void test01()
{
Person<string, int> p("Tom", 20);
p.showPerson();
}
int main() {
test01();
system("pause");
return 0;
}

类模板分文件编写

类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决:
● 解决方式1:直接包含.cpp源文件
● 解决方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制
推荐使用.hpp文件

#pragma  once 
#include <iostream>
#include <string>
using namespace std;
template<class T1 ,class T2>
class Person
{
public:
	Person(T1 name,T2 age);
	void showPerson();
	T1 m_Name;
	T2 m_Age;
};
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2  age)
{
	this->m_Name = name;
	this->m_Age = age;
}
template <class T1, class T2>
void Person<T1, T2>::showPerson()
{
	cout << "姓名:" << this->m_Name << "  年龄:  " << this->m_Age << endl;
}

类模板与友元

全局友元函数的实现方式:
● 类内实现
全局函数类内实现 - 直接在类内声明友元即可
● 类外实现
全局函数类外实现 - 需要提前让编译器知道全局函数的存在
类外实现步骤:
1、先做函数模板声明,下方再做函数模板定义,在做友元(函数类模板用到的类模板,则还得先做类声明)

//全局函数配合友元 类外实现 - 先做函数模板声明,下方在做函数模板定义,在做友元 
//函数类模板用到的类模板,则还得先做类声明
template<class T1, class T2> class Person; 
//如果声明了函数模板,可以将实现写到后面,否则需要将实现体写到类的前面让编译器提前看到 
template<class T1, class T2> void printPerson2(Person<T1,T2> & p) 

2、类内声明友元函数是函数模板

//friend void printPerson2(Person<T1, T2>& p);//这是普通函数
friend void printPerson2<>(Person<T1, T2>& p);//要声明这是函数模板,否则无法链接函数模板的实现

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

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

相关文章

centos上部署k8s

环境准备 四台Linux服务器 主机名 IP 角色 k8s-master-94 192.168.0.94 master k8s-node1-95 192.168.0.95 node1 k8s-node2-96 192.168.0.96 node2 habor 192.168.0.77 镜像仓库 三台机器均执行以下命令&#xff1a; 查看centos版本 [rootlocalhost Work]# cat /…

2024腾讯Java面试题精选,教你抓住面试的重点

重要 大环境对于我们能力要求越来越高&#xff0c;医学专家又说今年冬天新冠肺炎将“席卷重来”。 如果疫情再次爆发&#xff0c;势必将再次影响企业的正常运作&#xff0c;一波裁员浪潮你又能否抗住&#xff1f; 不管如何&#xff0c;明年金三银四又是一波跳槽时机&#xf…

数字化时代的新里程碑:Web3的革命

在当今数字化时代&#xff0c;Web3正成为了一股强大的力量&#xff0c;重新定义了我们对互联网的认知。本文将深入探讨Web3的定义、特点&#xff0c;以及它对金融、供应链、社交媒体等领域的革命性影响&#xff0c;并展望Web3的未来发展。 1. Web3的定义与特点 Web3不仅是一种…

力扣206反转链表

206.反转链表 力扣题目链接(opens new window) 题意&#xff1a;反转一个单链表。 示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL 1&#xff0c;双指针 2&#xff0c;递归。递归参考双指针更容易写&#xff0c; 为什么不用头插…

无代理方式实现VMware的迁移?详细解析

在当今数字化时代&#xff0c;数据的安全性和可用性对于企业至关重要。尤其是在VMware转变订阅策略后&#xff0c;原本永久订阅的产品转变为以年付费订阅的形式&#xff0c;导致客户不得不支付更多的费用&#xff0c;大幅增加了成本。同时&#xff0c;客户也对VMware未来发展前…

计算机图形学的作用

计算机图形学的作用 计算机图形学的作用1.创造数字世界2.物理世界的仿真模拟2.1 三维几何2.2 物理动态2.3 人体运动2.4 虚实融合 3.仿真模拟与智能应用 笔记来源&#xff1a;GAMES001-图形学中的数学 计算机图形学的作用 1.创造数字世界 计算机图形学创造数字世界 数字世界…

FEP容量瓶多应用于制药光电光伏行业

常用规格&#xff1a;25ml、50ml、100ml、250mlFEP容量瓶也叫特氟龙容量瓶&#xff0c;容量瓶是为配制一定物质的量浓度的溶液用的精确定容器皿&#xff0c;常和移液管配合使用。广泛用于ICP-MS、ICP-OES等痕量分析以及同位素分析等高端实验。地质、电子化学品、半导体分析测试…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:TapGesture)

支持单击、双击和多次点击事件的识别。 说明&#xff1a; 从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 接口 TapGesture(value?: { count?: number, fingers?: number }) 参数&#xff1a; 参数名称参数类型必填参…

【Android Studio】的矢量绘图【pathData】详解

目录&#xff1a; 例子老师&#xff1a;一、基础知识&#xff1a;1、命令和常数&#xff1a;2、绝对坐标和相对坐标&#xff1a; 一、落笔命令命令Mx&#xff0c;y和mx&#xff0c;y&#xff08;大小写绝对和相对&#xff09; 二、画直线命令Lx&#xff0c;y和lx&#xff0c;y&…

Linux系统——LVS-DR群集部署及拓展

目录 引言 1.LVS的工作模式及其工作过程 2.列举出LVS调度算法 3.LVS调度常见算法&#xff08;均衡策略&#xff09; 3.1固定调度算法:rr&#xff0c;wrr&#xff0c;dh&#xff0c;sh 3.2动态调度算法:wlc&#xff0c;lc&#xff0c;lblc 4.LVS三种工作模式区别 一、I…

逆向实战33——某东登录参数与流程分析(包含滑块)

声明 本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 目标网站 aHR0cHM6Ly9wYXNzcG9ydC5qZC5jb20vbmV3L2xvZ2luLmFzcHg/UmV0dXJuVXJsPWh0dHBzJ…

CSS极速入门

CSS介绍 什么是CSS? CSS(Cascading Style Sheet),层叠样式表,用于控制页面的样式. CSS能够对网页中元素位置的排版进行像素级的精确控制,实现美化页面的效果.能够做到页面的样式和结构分离. CSS可以理解为"东方四大邪术"的化妆术. 对页面展示进行化妆. 基本语法规…

PCM会重塑汽车OTA格局吗(2)

目录 1.概述 2. PCM技术视角下的OTA 3.小结 1.概述 上一篇文章&#xff0c;我们着重讲解了OTA的概述内容&#xff0c;和意法半导体推出的跨域融合MCU的四大特征&#xff0c;其中就包含了OTA技术。 他们针对OTA做了比较创新的设计&#xff0c;在总的可用memory容量不变情况…

Ansys Zemax | 如何在OpticStudio中建模DMD(MEMS)

附件下载 联系工作人员获取附件 什么是DMD/ MEMS 下图显示了一个DMD设备&#xff0c;它单独倾斜的微镜组成。镜子通常被称为像素。 如何在OpticStudio中建模DMD 这些设备可以在序列或非序列模式下建模。 如何计算单个像素/镜子的旋转 本节将说明如何设置单个像素的旋转。像…

FEP样品瓶透明聚四氟乙烯取样瓶

一、产品介绍 FEP试剂瓶&#xff0c;也叫FEP取样瓶、特氟龙样品瓶等&#xff0c;主要用于痕量分析、同位素检测&#xff0c;ICP-MS/OES/AAS分析等高端实验。本底值低&#xff0c;金属元素铅、铀含量小于0.01ppb,无溶出与析出。 常用尺寸&#xff08;ml&#xff09;&#xff1…

2024大厂Java面试最火问题,1200页文档笔记

前言 ⽂章有点⻓&#xff0c;请耐⼼看完&#xff0c;绝对有收获&#xff01;不想听我BB直接进⼊⾯试分享&#xff1a; 准备过程蚂蚁⾦服⾯试分享拼多多⾯试分享字节跳动⾯试分享最后总结个人所得&#xff08;供大家参考学习&#xff09; 当时我⾃⼰也准备出去看看机会&#…

七、链表问题(上)

160、相交链表&#xff08;简单&#xff09; 题目描述 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个…

引领测试开发新风向:模型驱动测试的魔力

测试开发是软件开发周期中至关重要的一个环节&#xff0c;而模型驱动测试作为一种新颖的测试方法&#xff0c;为测试开发带来了新的思路和技术。本文将探讨如何利用模型驱动测试优化测试开发流程&#xff0c;提高软件质量和开发效率。 模型驱动测试在测试开发中的应用价值 模型…

计算机三级——网络技术(综合题第一题)

笔记 标准分类的IP地址&#xff1a; 类别地址范围实际可用范围说明A类0~1271.0.0.1~126.255.255.2540代表任何地址&#xff0c;127为回环测试地址B类128~191128.1.0.0~191.254.0.0128.0.0.0和191.255.0.0为保留ipC类192~223192.0.1.0~223.255.254.0192.0.0.0和223.255.255.0…

java多线程编程(四)-----线程池

一.线程池的介绍 java中的池是非常重要的思想方法&#xff0c;比如内存池&#xff0c;进程池&#xff0c;连接池&#xff0c;常量池等等。本篇重点介绍java中的线程池。这里的这些池的概念都是一样的&#xff0c;比如做饭的时候&#xff0c;有烧水&#xff0c;切菜&#xff0c…