• C++函数模板学习笔记


    一、模板概述

     C++提供了函数模板,实际上就是建立一个通用的函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表,这个通用函数就成为函数模板,凡是函数体相同的函数都可以用这个模板代替,不必定义多个函数,只需要在模板中定义一次即可,在调用函数时系统会根据实参类型来取代模板中的虚拟类型,从而实现不同函数的功能。

    • C++提供两种模板机制:函数模板和类模板
    • 类属->类型参数化,又称为参数模板

    总结:

    • 模板把函数或者类要处理的数据类型参数化,表现为参数的多态性,成为类属
    • 模板用于表达逻辑结构相同,但是具体数据类型不同的数据对象的通用行为

    二、函数模板案例

    #define _CRT_SECURE_NO_WARNINGS
    #include
    using namespace std;
    
    // T代表泛型的数据类型 不能只是写T
    // 让编译器看到这句话后面紧跟着的函数里面有 T 不要报错
    template<class T>
    void mySwap(T& a, T& b)
    {
    	T temp = a;
    	a = b;
    	b = temp;
    }
    
    // 使用函数模板
    void test02()
    {
    	int a = 10;
    	int b = 20;
    	mySwap(a, b);
    
    	cout << "a = "<< a << endl;
    	cout << "b = " << b << endl;
    }
    
    
    int main()
    {
    	test02();
    	return EXIT_SUCCESS;
    }
    
    • 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
    • 编译器会自动根据实参推导参数类型
    • 也可以显示的指定类型 使用参数列表
    • 指定类型,传入时必须符合要求
    • 调用时,必须让编译器知道泛型T具体是什么类型
    • 在调用函数模板的时候,编译器会进行第二次编译,确定具体是什么数据类型的函数模板调用
    #define _CRT_SECURE_NO_WARNINGS
    #include
    using namespace std;
    
    // T代表泛型的数据类型 不能只是写T
    // 让编译器看到这句话后面紧跟着的函数里面有 T 不要报错
    template<class T>
    void mySwap(T& a, T& b)
    {
    	T temp = a;
    	a = b;
    	b = temp;
    }
    
    // 使用函数模板
    void test02()
    {
    	int a = 10;
    	int b = 20;
    
    	// 编译器会根据实参来自动推导数据类型
    	mySwap(a, b);
    	/*
    	编译器在函数模板被调用的时候,进行第二次编译  确定具体数据类型
    	void mySwap(int &a,int &b)
    	{
    	int tmp = a;
    	a = b;
    	b = tmp;
    	*/
    
    	cout << "a = "<< a << endl;
    	cout << "b = " << b << endl;
    		
    	// 显示的指定类型
    	mySwap<int>(a, b);//<> 使用参数列表
    }
    
    int main()
    {
    	test02();
    	return EXIT_SUCCESS;
    }
    
    
    • 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

    如果使用参数列表指定数据类型,那么实参就可以进行隐式转换

    #define _CRT_SECURE_NO_WARNINGS
    #include
    using namespace std;
    
    // T代表泛型的数据类型 不能只是写T
    // 让编译器看到这句话后面紧跟着的函数里面有 T 不要报错
    template<class T>
    T func(T a, T b)
    {
    	return a + b;
    }
    
    void test03()
    {
    	int a = 10;
    	double b = 20.2;
    
    	// 指定数据类型 int  这里的double会自动进行数据转换
    	cout << func<int>(a, b);// 打印30
    }
    
    int main()
    {
    	test03();
    	return EXIT_SUCCESS;
    }
    
    • 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

    可以使用typename定义函数模板

    template<typename T>
    void func(T a, T b)
    {
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    三、函数模板排序

    这里使用函数模板编写一个冒泡排序算法,适用于任何类型的数组

    #define _CRT_SECURE_NO_WARNINGS
    #include
    using namespace std;
    
    // T代表泛型的数据类型 不能只是写T
    // 让编译器看到这句话后面紧跟着的函数里面有 T 不要报错
    template<class T>
    void mySort(T arr[], int len)
    {
    	// 冒泡排序
    	for (int i = 0; i < len; i++)
    	{
    		for (int j = 0; j < len - i - 1; j++)
    		{
    			if (arr[j] > arr[j + 1])
    			{
    				T tmp = arr[j];
    				arr[j] = arr[j + 1];
    				arr[j + 1] = tmp;
    			}
    		}
    	}
    }
    
    // 使用函数模板  打印数组
    template<class T>
    void myPrint(T arr[], int len)
    {
    	for (int i = 0; i < len; i++)
    	{
    		cout << arr[i] << " ";
    	}
    	cout << endl;
    }
    
    void test03()
    {
    	// 可以传入任意类型的数组进入函数模板
    	char arrchar[] = "hello world";
    	int len = sizeof(arrchar) / sizeof(char);
    	mySort(arrchar, len);
    	myPrint(arrchar,len);
    
    	int arrInt[] = {1,34,25,36,546,12,11};
    	len = sizeof(arrInt) / sizeof(int);
    	mySort(arrInt, len);
    	myPrint(arrInt, len);
    }
    
    int main()
    {
    	test03();
    	return EXIT_SUCCESS;
    }
    
    
    • 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

    三、普通函数和函数模板的区别

    • 普通函数可以直接进行隐式转换,但是函数模板不能直接进行隐式转换,必须指定参数列表,然后实参根据参数列表进行隐式转换
    #define _CRT_SECURE_NO_WARNINGS
    #include
    using namespace std;
    
    // 普通函数
    int myPlus(int a, int b)
    {
    	return a + b;
    }
    
    template<class T>
    int myPlus2(T a, T b)
    {
    	return a + b;
    }
    
    void test()
    {
    	int a = 10;
    	int b = 20;
    	char c = 'a';// 转换成int 数据类型 变为97
    
    	// 普通函数可以直接进行隐式转换
    	int d = myPlus(a,c);
    	cout << d << endl;
    
    	// 函数模板不可以直接进行隐式转换
    	int e = myPlus2<int>(b,c);
    	cout << e << endl;
    }
    
    int main()
    {
    	test();
    	return EXIT_SUCCESS;
    }
    
    • 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

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

    函数模板和普通函数可以进行重载

    #define _CRT_SECURE_NO_WARNINGS
    #include
    using namespace std;
    
    // 普通函数
    int myPlus(int a, int b)
    {
    	return a + b;
    }
    
    template<class T>
    int myPlus(T a, T b)
    {
    	return a + b;
    }
    
    void test()
    {
    	int a = 10;
    	int b = 20;
    	char c = 'a';// 转换成int 数据类型 变为97
    
    	// 普通函数可以直接进行隐式转换
    	int d = myPlus(a,c);
    	cout << d << endl;
    
    	// 函数模板不可以直接进行隐式转换
    	int e = myPlus<int>(b,c);
    	cout << e << endl;
    }
    
    int main()
    {
    	test();
    	return EXIT_SUCCESS;
    }
    
    • 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

    但是普通函数和函数模板进行重载的时候,编译器优先使用,普通函数,避免二次编译,所以要使用函数模板,还是需要使用参数列表

    函数模板和函数模板之间也可以进行重载

    如果函数模板可以产生更好的匹配,那么优先使用函数模板

    五、函数模板的局限性和解决办法

    如果传入的是数组名,那么函数模板中比较函数名的大小就没有意义,因为这里数组名是地址,所以他们比较的是地址。

    #define _CRT_SECURE_NO_WARNINGS
    #include
    using namespace std;
    
    template<class T>
    void func(T a, T b)
    {
    	if (a > b)
    	{
    		cout << "a > b" << endl;
    	}
    	else
    	{
    		cout << "a <=  b" << endl;
    	}
    }
    
    void test()
    {
    	int arr[20];
    	int arr2[20];
    
    	func(arr,arr2);
    }
    
    int main()
    {
    	test();
    	return EXIT_SUCCESS;
    }
    
    
    • 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

    总之,编写的模板函数很可能没办法处理某些数据类型,另一方面,有时候通用化是有意义的。模板的重载可以为这些特定的类型提供具体化的模板

    六、函数模板的具体化

    • 不建议具体化函数模板,因为没有通用性
    • 具体化函数模板,注意上面的函数模板要有,才可以具体化
    #define _CRT_SECURE_NO_WARNINGS
    #include
    #include
    using namespace std;
    
    class Maker
    {
    public:
    	Maker(string name,int age)
    	{
    		this->name = name;
    		this->age = age;
    	}
    public:
    	string name;
    	int age;
    };
    
    template<class T>
    void myfunc(T& a, T& b)
    {
    	if (a > b)
    	{
    		cout << "a > b" << endl;
    	}
    	else
    	{
    		cout << "a < b" << endl;
    	}
    }
    
    // 函数模板的具体化 先写函数模板 才能具体化  指定参数列表  然后传入指定参数
    template<>void myfunc<Maker>(Maker& a, Maker& b)
    {
    	cout << "函数模板的具体化" << endl;
    	if (a.age > b.age)
    	{
    		cout << "a > b" << endl;
    	}
    	else
    	{
    		cout << "a < b" << endl;
    	}
    }
    
    void test()
    {
    	Maker a("z",1);
    	Maker b("a",11);
    	myfunc(a,b);
    }
    
    int main()
    {
    	test();
    	return EXIT_SUCCESS;
    }
    
    • 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
  • 相关阅读:
    Spark学习笔记(一):基于Kubernetes安装Spark
    java基于springboot中小企业合同管理系统
    人工神经网络算法的应用,人工神经网络是算法吗
    手把手教你用Python绘制神经网络图
    4月25日,每日信息差
    串的匹配 (KPM算法)
    Codeforces Round 899 (Div. 2) E1. Two Permutations (Easy Version) (思维题/交换序列)
    防御—IPsecVPN
    Centos6 密钥登陆,解决所选的用户密钥未在远程主机上注册
    深度学习之 14 深度学习前沿与局限
  • 原文地址:https://blog.csdn.net/qq_44653420/article/details/127709318