• 30C++编程提高篇-----1、函数模板原理


    C++提高编程

    • 本阶段主要针对C++范式编程STL技术做详细讲解,探讨C++更深层的使用。

    10 模板

    10.1 模板的概念

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

    例如:生活中的模板

    一寸照片模板:

    在这里插入图片描述

    PPT模板

    在这里插入图片描述

    模板的特点:

    • 模板不可以直接使用,它只是一个框架
    • 模板的通用性并不是万能的,不同情形需要不同的模板

    10.2 函数模板

    • C++中除了面向对象编程思想外还有一种称为范式编程的思想,主要利用的技术就是模板
    • C++提供两种模板机制:函数模板和类模板
    10.2.1 函数模板语法

    函数模板作用:

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

    语法:

    template<typename T>
    函数声明或定义
    
    • 1
    • 2

    解释:

    template--------声明创建模板

    tymplate-------表面其后面的符号是一种数据类型,可以用class 代替

    T ------ 通用的数据类型,名称可以替换,通常为大写字母

    示例代码:

    #include
    using namespace std;
    
    //函数模板
    //普通函数写法
    void swapInt(int &a,int &b)  //地址传递
    {
    	int temp;
    	temp = a;
    	a = b;
    	b = temp;
    }
    
    void swapDouble(double& a, double& b)
    {
    	double temp;
    	temp = a;
    	a = b;
    	b = temp;
    }
    
    void test01()
    {
    	int a = 10;
    	int b = 20;
    	swapInt(a, b);
    	cout << "a = " << a << endl;
    	cout << "b = " << b << endl;
    
    	double c = 1.1;
    	double d = 2.2;
    	swapDouble(c, d);
    	cout << "c = " << c << endl;
    	cout << "d = " << d << endl;
    
    }
    
    //模板
    template<typename T>//声明一个模板,告诉编译器后面代码中紧跟着的T不要报错,T是一个通用的数据类型
    void mySwap(T& a,T &b)
    {
    	T temp = a;
    	a = b;
    	b = temp;
    }
    
    //利用函数模板实现两个数交换
    void test02()
    {
    	int a = 10;
    	int b = 20;
    
    	//两种方式使用函数模板
    	//1、自动类型推导
    	
    	mySwap(a, b);
    	cout << "a = " << a << endl;
    	cout << "b = " << b << endl;
    
    	//指定模板类型
    	double c = 1.1;
    	double d = 2.2;
    	//mySwap(c, d);
    	mySwap<double>(c, d);
    	cout << "c = " << c << endl;
    	cout << "d = " << d << endl;
    }
    
    int main()
    {
    	//test01();
    	test02();
    	system("pause");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    运行结果:

    在这里插入图片描述

    总结:

    • 函数模板使用关键字template
    • 使用函数模板有两种方式:1、自动类型推导 2、显示指定类型
    • 模板的目的是为了提高代码复用性,将类型参数化
    10.2.2 函数模板注意事项

    注意事项:

    • 1、自动类型推导,必须推导出一致的数据类型T,才可以使用
    • 2、模板必须要确定出T的数据类型,才可以使用

    示例代码:

    #include
    using namespace std;
    
    //函数模板注意事项
    
    template<class T>	//typename可以替换成class,class是通用的用class肯定没错
    
    void Swap(T&a,T&b)
    {
    	T temp=a;
    	a = b;
    	b = temp;
    }
    
    
    //1、自动类型推导,必须推导出一致的数据类型T才可以使用
    void test21()
    {
    	int a = 10;
    	int b = 20;
    	//Swap(a, b);
    	char ch = 'c';
    	//Swap(a, ch);
    	cout << "a = " << a << endl;
    	cout << "b = " << b << endl;
    }
    
    //2、模板必须要确定出T的数据类型,才可以使用
    template<class T>
    void func()
    {
    	cout << "这是fun()调用" << endl;
    }
    
    void test22()
    {
    	func<char>();
    }
    
    int main()
    {
    	//test21();
    	test22();
    	system("pause");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    运行结果:

    在这里插入图片描述

    10.2.3 函数模板案例

    案例描述:

    • 利用函数模板封装一个排序的函数,可以对不同数据类型数组进行排序
    • 排序规则从大到小,排序算法为选择排序
    • 分别利用char数组int数组进行测试

    示例代码:

    #include
    using namespace std;
    
    //实现通用  对数组进行排序的函数
    //规则 从大到小
    //算法 选择
    //测试 char 数组、int数组
    
    //交换模板
     template<class T>
     void mySwap(T &a,T &b)
     {
    	 T temp;
    	 temp = a;
    	 a = b;
    	 b = temp;
     }
    
    //排序算法
    template<class T>
    void mySort(T arr[], int len)
    {
    	for (int i = 0; i < len; i++)
    	{
    		int max = i;		//认定最大值的下标
    		for (int j = i+1; j < len; j++)
    		{
    			//认定的最大值 比 遍历出的数值要小,说明j下标的元素才是真正的最大值
    			if (arr[max]<arr[j])
    			{
    				max = j;	//更新最大值下标
    			}
    		}
    		if (max!=i)
    		{
    			//交换max和i元素
    			mySwap(arr[i], arr[max]);
    		}
    	}
    }
    
    //提供一个打印数组的模板
    template<class T>
    void printArray(T arr[], int len)
    {
    	for (int i = 0; i < len; i++)
    	{
    		cout << arr[i] << " ";
    	}
    	cout << endl;
    }
    
    void test31()
    {
    	char charArr[] = "fdbae";
    	int len = sizeof(charArr) / sizeof(char);
    	mySort(charArr,len);
    	//cout << charArr << endl;
    	printArray(charArr, len);
    }
    
    void test32()
    {
    	//测试int数组
    	int intArr[] = {7,8,1,5,9,4,3,2,0};
    	int len = sizeof(intArr) / sizeof(int);
    	mySort(intArr, len);
    	printArray(intArr, len);
    }
    
    int main()
    {
    	//test31();
    	test32();
    	system("pause");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    运行结果:

    在这里插入图片描述

    10.2.4 普通函数与函数模板的区别

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

    • 普通函数调用时可以发生自动类型转换(隐式类型转换)
    • 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
    • 如果利用显示指定类型的方式,可以发生隐式类型转换

    示例代码:

    #include
    using namespace std;
    
    //普通函数与函数模板区别
    
    //1、普通函数调用可以发生隐式类型转换
    //2、函数模板  用自动类型推导,不可以发生隐式类型转换
    //3、函数模板  用显示指定类型,可以发生隐式类型转换
    
    //普通函数
    int myAdd01(int a, int b)
    {
    	return a + b;
    }
    
    //函数模板
    template<class T>
    T myAdd02(T a, T b)
    {
    	return a + b;
    }
    
    void test41()
    {
    	int a=10;
    	int b = 10;
    	char c = 'a';
    	//cout << myAdd01(a, c) << endl;  //myAdd01()在调用时自动把字符c隐式转换为int类型进行计算
    	//int sum=myAdd02(a,b);		//自动推导方式
    	//int sum = myAdd02(a, c);		//发生报错,无法进行隐式转换
    	//cout << sum << endl;
    
    	//显示指定类型,会发生隐式转换
    	cout << myAdd02<int>(a, c) << endl;
    }
    
    int main()
    {
    	test41();
    	system("pause");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    运行结果:

    在这里插入图片描述

    **总结: **建议实验显示指定类型的方式来调用函数模板,因为自己确定通用类型T

    10.2.5 普通函数与函数模板的调用规则

    调用规则如下:

    1. 如果函数模板和普通函数都可以实现,优先调用普通函数
    2. 可以通过空模板参数列表来强制调用函数模板
    3. 函数模板也可以发生重载
    4. 如果函数模板可以产生更好的匹配,优先调用函数模板

    示例代码:

    #include
    using namespace std;
    
    //普通函数与函数模板调用规则
    //1、如果函数模板和普通函数都可以调用,编译器优先调用普通
    //2、可以通过空模板参数列表,强制调用函数模板
    //3、函数模板可以发生函数重载
    //4、如果函数模板可以产生更好的匹配,优先调用函数模板
    
    void myPrint(int a, int b)
    {
    	cout << "调用的普通函数" << endl;
    }
    
    template<class T>
    void myPrint(T a, T b)
    {
    	cout << "调用的模板" << endl;
    }
    
    template<class T>
    void myPrint(T a, T b,T c)
    {
    	cout << "调用重载的模板" << endl;
    }
    
    void test51()
    {
    	int a = 10;
    	int b = 40;
    	//myPrint(a, b);//优先调用普通函数
    	//myPrint(a, b);
    
    	//myPrint(a, b, 100);
    	// 
    	//如果函数模板产生更好的匹配,优先调用函数模板
    	char e= 'a';
    	char f = 'b';
    	myPrint(e, f);
    }
    
    int main()
    {
    	test51();
    	system("pause");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    运行结果:

    在这里插入图片描述

    **总结: **既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性。

    10.2.6 模板的局限性

    局限性

    • 模板的通用性并不是万能的

    例如:

    template<class T>
    void f(T a,T b)
    {
    	a=b;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在上述中提供的赋值操作,如果传入的a和b是一个数组,就无法实现了

    再例如:

    template
    void f(T a,T b)
    {
    	if(a>b){.....}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    上述代码中,如果T的数据类型传入的是像Person这样的自定义数据类型,也无法正常运行,因此C++为了解决这种问题,提高模板的重载,可以为这些特定的类型提供具体化的模板。

    示例代码:

    #include
    using namespace std;
    
    //模板的局限性
    //模板并不是万能的,有些特定的数据类型,需要用具体化方式做特殊实现
    
    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;
    	}
    	else
    	{
    		return false;
    	}
    }
    
    //利用具体化Person的版本实现代码,具体化优先调用
    template<> bool myCompare(Person& p1, Person& p2)
    {
    	if (p1.m_name == p2.m_name && p1.m_age == p2.m_age)
    	{
    		return true;
    	}
    	else
    	{
    		return false;
    	}
    }
    
    void test61()
    {
    	int a = 10;
    	int b = 20;
    	bool ret = myCompare(a, b);
    	if (ret)
    	{
    		cout << "a == b" << endl;
    	}
    	else
    	{
    		cout << "a != b" << endl;
    	}
    }
    
    void test62()
    {
    	Person p1("Tom", 10);
    	Person p2("Tom", 10);
    
    	bool ret = myCompare(p1, p2);
    	if (ret)
    	{
    		cout << "p1 == p2" << endl;
    	}
    	else
    	{
    		cout << "p1 != p2" << endl;
    	}
    }
    
    int main()
    {
    	//test61();
    	test62();
    	system("pause");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    运行结果:

    在这里插入图片描述

    总结:

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

    10.3 类模板

    1.3.1 类模板

    类模板作用:

    • 建立一个通用类,类中的成员,数据类型可以不具体制定,用一个虚拟的类型来代表。

    语法:

    template<typename T>
    • 1
    • 2

    解释:

    template------------声明创建模板

    typename ---------表明其后面的符号是一种数据类型,可以用class 代替

    T --------- 通用的数据类型,名称可以替换,通常为大写字母

    示例代码:

    示例代码:

    #include
    using namespace std;
    
    //模板的局限性
    //模板并不是万能的,有些特定的数据类型,需要用具体化方式做特殊实现
    
    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;
    	}
    	else
    	{
    		return false;
    	}
    }
    
    //利用具体化Person的版本实现代码,具体化优先调用
    template<> bool myCompare(Person& p1, Person& p2)
    {
    	if (p1.m_name == p2.m_name && p1.m_age == p2.m_age)
    	{
    		return true;
    	}
    	else
    	{
    		return false;
    	}
    }
    
    void test61()
    {
    	int a = 10;
    	int b = 20;
    	bool ret = myCompare(a, b);
    	if (ret)
    	{
    		cout << "a == b" << endl;
    	}
    	else
    	{
    		cout << "a != b" << endl;
    	}
    }
    
    void test62()
    {
    	Person p1("Tom", 10);
    	Person p2("Tom", 10);
    
    	bool ret = myCompare(p1, p2);
    	if (ret)
    	{
    		cout << "p1 == p2" << endl;
    	}
    	else
    	{
    		cout << "p1 != p2" << endl;
    	}
    }
    
    int main()
    {
    	//test61();
    	test62();
    	system("pause");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    运行结果:

    在这里插入图片描述

    在这里插入图片描述

    谢谢你的点赞,评论和转发 ( ^ o ^)/~。

    上一篇:29手把手教你用C++写一个职工管理系统(实战派)

    下一篇:31C++编程提高篇----2、类模板原理

  • 相关阅读:
    keepalived 主备都存在vip, keepalived主备跨网段配置;keepalived主备服务器不在同一个网段怎么配置
    Redis6笔记04 主从复制,集群,应用问题,Redis6新功能
    elementPlus + table 树形懒加载 新增,删除,修改 局部刷新
    百度之星(夏日漫步)
    不知道吧?未加工的食物可以帮助你减肥
    如何在centos上安装nvidia docker
    C/C++ 进阶(4)二叉搜索树
    一文详解贝叶斯优化(Bayesian Optimization)原理
    【OpenCV】【计算机图形学】DIBR: Depth Image Based Rendering/ 3D image warping 中的实现细节
    ASEMI肖特基二极管MBR10200CT在电子电路中起什么作用
  • 原文地址:https://blog.csdn.net/qq_45986997/article/details/126556058