您的位置:首页 > 财经 > 金融 > C++系列-泛型编程概念及函数模板

C++系列-泛型编程概念及函数模板

2024/11/19 16:30:34 来源:https://blog.csdn.net/weixin_48668114/article/details/141498494  浏览:    关键词:C++系列-泛型编程概念及函数模板

泛型编程概念及函数模板

  • 泛型编程的引入
  • 模板
    • 函数模板
    • 函数模板注意事项
    • 函数模板举例
    • 普通函数和函数模板的区别
      • 隐式类型转换上的区别
      • 调用上的规则和区别
    • 模板的局限性
    • 学习模板的意义


白云泉
唐代:白居易
天平山上白云泉,云自无心水自闲。
何必奔冲山下去,更添波浪向人间。


泛型编程的引入

想要比较两个整形数据的大小,写一个形参数是整形的函数。想要比较两个浮点型数据的大小,写一个形参是浮点型的函数。想要比较两个字符的ascii码的大小,写一个形参是char的函数。实际上几个函数的代码都差不多,那你觉得啰里八嗦写了一堆代码,烦不烦?
所以,如果可以把数据类型也作为一种可以灵活传递的参数,就太人性了。这种编程就是泛型编程了。

泛型,广泛的类型,指的仍是一种数据类型,它是一种通用类型,它可以代替其它多种类型,这个方法可以大规模的减少程序代码的编写量和重复性。泛型编程利用的主要技术是模板。

模板

C++中有两种模板机制:函数模板,类模板

函数模板

  • 函数的返回类型和形参的类型可以不具体指定,用代号T来表示,T称为泛型(generic type)。使用这种类型占位符的编程方式就叫泛型编程。
  • 在函数调用的时候再去确认T的实际类型。
  • 语法: template, 在有了这个声明后,其下紧跟的函数就是一个函数模板。template:声明创建模板,typename:表明其后的符号代表一种数据类型,也可以用class代替。后面可以有多个typename。T:通用数据类型,也可以用其它符号代替。
  • 函数模板的两种实现方式
    – 自动类型转换
    – 显式指定类型,推荐。
code:
# include <iostream>
using namespace std;template <typename T>	// 声明一个模板,告诉编译器后面代码中紧跟着的T是一个通用数据模型	
T max_2(T a, T b)		// T是抽象的一个类型,在使用的时候再确定具体类型
{T result = a > b ? a : b;cout << "the max value: " << result << endl;return result;
}template <typename T>	// 声明一个模板,告诉编译器后面代码中紧跟着的T是一个通用数据模型
void swap_2(T &a, T &b)	// T是抽象的一个类型,在使用的时候再确定具体类型
{T temp = a;a = b;b = temp;
}int main()
{max_2(8, 5);max_2(8.6, 9.5);int a = 666;int	b = 888;cout << "原始的a: " << a << ", 原始的b: " << b << endl;swap_2(a, b);		// 模板实现的方式是自动类型转换cout << "交换后的a: " << a << ", 交换后的b: " << b << endl;float c = 666.666;float d = 888.888;cout << "原始的c: " << c << ", 原始的d: " << d << endl;swap_2<float>(c, d);		// 模板实现的方式是显式指定类型cout << "交换后的c: " << c << ", 交换后的d: " << d << endl;system("pause");return 0;
}result:
the max value: 8
the max value: 9.5
原始的a: 666, 原始的b: 888
交换后的a: 666, 交换后的b: 888
原始的c: 666.666, 原始的d: 888.888
交换后的c: 888.888, 交换后的d: 666.666

函数模板注意事项

  • 如果使用自动类型推导,如果只使用类型T,则必须要能推导出一致的数据类型T。
  • T必须要有确定的类型,可以使用显示指示类型方式直接指定。
code:
#include <iostream>
using namespace std;template <typename T>
void swap_1(T& a, T& b)	// T是抽象的一个类型,在使用的时候再确定具体类型
{T temp = a;a = b;b = temp;
}template <typename T>
void print_info()
{cout << "print_info函数" << endl;
}int main()
{int a = 11;	int b = 22;char c = 'g';//swap_1(a, c);			// 错误,在模板中类型T,函数调用是有int,有char,无法推导出T的类型cout << "原始的a: " << a << ", 原始的b: " << b << endl;swap_1(a, b);			// 模板实现的方式是自动类型转换cout << "交换后的a: " << a << ", 交换后的b: " << b << endl;// print_info();		// 错误,没有确定的T,无法使用自动类型推导的方式print_info<int>();		// 使用显示指定类型,T有了确定的类型system("pause");return 0;
}result:
原始的a: 11, 原始的b: 22
交换后的a: 22, 交换后的b: 11
print_info函数

函数模板举例

code:
#include <iostream>
using namespace std;
template <typename T>
void my_swap(T &a, T &b)
{T temp = a;a = b;b = temp;
}template <typename T>
void my_sort(T array[], int num)	// T是抽象的一个类型,在使用的时候再确定具体类型
{for(int i_loop=0; i_loop<num; i_loop++){int max_idx = i_loop;for (int j_loop = i_loop + 1; j_loop < num; j_loop++){if (array[j_loop] > array[max_idx]){my_swap(array[j_loop], array[max_idx]);}}}
}template <typename T>
void print_info(T array[], int num)
{for (int i_loop = 0; i_loop < num; i_loop++){cout << array[i_loop] << " ";}cout << endl;
}template <typename T>
void test01(T array, int num)		// 这里的T是个指针类型的
{cout << "排序前的数据: ";print_info(array, num);my_sort(array, num);cout << "排序后的数据: ";print_info(array, num);
}int main()
{int array1[] = { 1,0,-2,4,8,10,6,9,44 };int num1 = sizeof(array1) / sizeof(array1[0]);char array2[] = "afgbdozs";int num2 = sizeof(array1) / sizeof(array1[0]);test01(array1, num1);test01<char*>(array2, num2);system("pause");return 0;
}result:
排序前的数据: 1 0 -2 4 8 10 6 9 44
排序后的数据: 44 10 9 8 6 4 1 0 -2
排序前的数据: a f g b d o z s
排序后的数据: z s o g f d b a

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

隐式类型转换上的区别

  • 普通函数调用时可以发生自动类型转换(隐式类型转换)。
  • 函数模板调用时,如果使用自动类型推导,不发生隐式类型转换。
  • 函数模板调用时,如果使用显式指定类型的方式,可以发生隐式类型转换。
code:
#include <iostream>
using namespace std;
template <typename T>
void my_swap(T& a, T& b)
{T temp = a;a = b;b = temp;
}void my_add01(int a, int b)
{	cout << "add result: " << a + b << endl;
}template <typename T>
void my_add02(T a, T b)	// T是抽象的一个类型,在使用的时候再确定具体类型
{cout << "add result: " << a + b << endl;
}int main()
{int a = 68;int b = 22;char c = 'a';cout << "普通函数调用" << endl;my_add01(a, b);my_add01(a, c);cout << "函数模板自动类型推导调用" << endl;my_add02(a, b);// my_add02(a, c);		// 错误,无法实现隐式类型转换,不知道替换成是int还是charcout << "函数模板显式指定类型调用" << endl;my_add02<int>(a, b);my_add02<int>(a, c);system("pause");return 0;
}result:
普通函数调用
add result: 90
add result: 165
函数模板自动类型推导调用
add result: 90
函数模板显式指定类型调用
add result: 90
add result: 165

调用上的规则和区别

既然有函数模板,就不要再提供普通函数了,不明确。实际开发中最好不要同时出现二者

  • 如果普通函数和函数模板都可以调用,优先普通函数。
  • 可以使用显式指定类型的方式明确调用函数模板,也可以在类型指定中指定为空,强制调用。
  • 函数模板也可以实现重载。
  • 如果函数模板产生更好的匹配,则优先调用函数模板。
code:
// 如果普通函数和函数模板都可以调用,优先普通函数。
#include <iostream>
using namespace std;void print_info(int a)
{cout << "调用的是普通函数" << endl;
}template<typename T>
void print_info(T a)
{cout << "T的类型" << typeid(T).name() << endl;
}template<typename T1, typename T2>
void print_info(T1 a, T2 b)
{cout << "T1的类型" << typeid(T1).name() << endl;cout << "T2的类型" << typeid(T2).name() << endl;
}int main()
{cout << "---------- 1 -----------" << endl;print_info(3);			// 默认是调用普通函数的cout << "---------- 2 -----------" << endl;print_info<>(3);		// 通过空模板参数列表实现函数模板的调用,也可以显式指定类型法cout << "---------- 3 -----------" << endl;print_info<int, char>(33, 'a');		// 函数模板也可以发生重载cout << "---------- 4 -----------" << endl;print_info<int>(66);				// 函数模板也可以发生重载cout << "---------- 5 -----------" << endl;// 如果使用函数模板,则会直接将T指定为char,而如果使用普通函数,则需要进行隐式类型转换// 函数模板产生更好的匹配,则优先调用函数模板system("pause");print_info('c');		return 0;
}result:
---------- 1 -----------
调用的是普通函数
---------- 2 -----------
T的类型int
---------- 3 -----------
T1的类型int
T2的类型char
---------- 4 -----------
T的类型int
---------- 5 -----------

模板的局限性

  • 模板并非是万能的,有些特定的数据类型,需要做可特殊的实现。
    先看如下代码:
    如果func1中传的参数是数组,则在调用阶段会出错。
    如果func2中传的参数是类的对象,按照什么比较大小?
template<typename T>
void func1(T a, T b)
{a = b;
}template<typename T>
T func2(T a, T b)
{return a > b ? a : b;
}

利用具体化的模板,可以解决自定义类型的通用化

  • 下面代码中,在my_compare函数中,如果传递的参数是Person类型的,是无法比较大小的,所以单独对Person类型的实现具体化的代码,则当传递的参数是Person类型时,会走具体化的代码。
  • template<> bool my_compare(Person a, Person b), 前面要有template<> ,函数定义中的参数类型直接指明为Person。
code:
#include <iostream>
using namespace std;class Person
{
public:Person(string name, int age){m_name = name;m_age = age;}string m_name;int m_age;
};template<typename T>
bool my_compare(T a, T b)
{cout << "T的类型: " << typeid(T).name() << endl;//return (a == b) ? true : false;		// 如果T是Person,这里的比较会报错,无法用==比较,除非==重载return true;
}// 利用具体化的Person的代码实现,具体化调用,当传递T是Person时,走这段代码
template<>
bool my_compare(Person a, Person b)
{if ((a.m_name == b.m_name) && (a.m_age == b.m_age)){return true;}else{return false;}
}void test01()
{Person p1("Jerry", 2);Person p2("Tom", 5);cout << (my_compare(p1, p2) ? "a==b" : "a!=b") << endl;my_compare(3, 5);
}
int main()
{test01();system("pause");return 0;
}result:
a!=b
T的类型: int

学习模板的意义

  • 一般不是为了写模板,而是在STL能够运用系统提供的模板。

注意:本文部分内容来自黑马

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com