目录
模板的实用性很强,但不能完全完全复制使用
c++提供另一种编程思想? 泛型编程? 主要利用模板
模板是C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。
模板是一种对类型进行参数化的工具;
通常有两种形式:函数模板和类模板;
函数模板针对仅参数类型不同的函数;
类模板针对仅数据成员和成员函数类型不同的类。
使用模板的目的就是能够让程序员编写与类型无关的代码。比如编写了一个交换两个整型int 类型的swap函数,这个函数就只能实现int 型,对double,字符这些类型无法实现,要实现这些类型的交换就要重新编写另一个swap函数。使用模板的目的就是要让这程序的实现与类型无关,比如一个swap模板函数,即可以实现int 型,又可以实现double型的交换。模板可以应用于函数和类。
注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。
语法:
template<typename T>? // typename 也可以是? class
函数声明或定义
// 注释
template?? --- 声明创建模板
typename?? --- 表明其后面的符号是一种数据类型,可以用class代替
T????????? --- 通用数据类型,名称可以替换,通常为大写字母
示例:
#include<iostream>
using namespace std;
?
// 函数模板
?
// 两个整形交换函数
void swp_int(int &a,int &b)
{
int temp = a;
a=b;
b=temp;
}
?
// 交换两个浮点型的函数
void swp_double(double &a,double &b)
{
double temp =a;
a=b;
b=temp;
}
?
// 函数模板
template<typename T>? // 声明一个模板,告诉编译器中紧跟着T不要报错
void my_Swap(T &a,T &b)
{
T temp =a;
a=b;
b=temp;
}
?
void test01()
{
int a=10;
int b=20;
cout<<"交换前 a = "<<a<<" b = "<<b<<endl;
swp_int(a,b);
cout<<"交换后 a = "<<a<<" b = "<<b<<endl;
?
// 利用函数模板
// 两种方法使用函数模板
// 1. 自动类型推导
cout<<"自动--模板函数--交换前 a = "<<a<<" b = "<<b<<endl;
my_Swap(a,b);
cout<<"自动--模板函数--交换后 a = "<<a<<" b = "<<b<<endl;
?
// 2. 显示指定类型
cout<<"显示--模板函数--交换前 a = "<<a<<" b = "<<b<<endl;
my_Swap<int>(a,b);
cout<<"显示--模板函数--交换后 a = "<<a<<" b = "<<b<<endl;
?
?
double c =12.3;
double d = 23.4;
cout<<"交换前 c = "<<c<<" d = "<<d<<endl;
swp_double(c,d);
cout<<"交换后 c = "<<c<<" d = "<<d<<endl;
?
// 1. 自动类型推导
cout<<"自动--模板函数--交换前 c = "<<c<<" d = "<<d<<endl;
my_Swap(c,d);
cout<<"自动--模板函数--交换后 c = "<<c<<" d = "<<d<<endl;
?
// 2. 显示指定类型
cout<<"显示--模板函数--交换前 c = "<<c<<" d = "<<d<<endl;
my_Swap<double>(c,d);
cout<<"显示--模板函数--交换后 c = "<<c<<" d = "<<d<<endl;
}
?
int main()
{
test01();
return 0;
}
?
运行结果:
总结:
- 函敌模板利用关键字template
- 使用函数模板有两种方式:自动类型推导、显示指定类型
- 模板的目的是为了提高复用性,将类型参数化
?
?
注意事项
- 自动推导类型,必须推导出一致的数据类型T,才可以使用
- 模板必须要确定出T的数据类型,才可以使用
示例:
#include<iostream>
using namespace std;
// 注意事项
// 1. 自动推导类型,必须推导出一致的数据类型T,才可以使用
template<class T> // typename 可以替换成class
void my_Swap(T &a,T &b)
{
T temp =a;
??? a=b;
??? b=temp;
}
void test01()
{
int a = 10;
??? int b = 20;
char c = 'c';
?? ?// 必须是一致的数据类型才能使用
??? // 如果一个是int一个是char则使用不了
??? cout<<"自动--模板函数--交换前 a = "<<a<<" b = "<<b<<endl;
??? my_Swap(a,b);
// my_Swap(a,c);// 推到不出一致的T类型
??? cout<<"自动--模板函数--交换后 a = "<<a<<" b = "<<b<<endl;
}
// 2. 模板必须要确定出T的数据类型,才可以使用
template<typename T>
void func()
{
cout<<"func函数调用"<<endl;
}
void test02()
{
??? // 可以强制给他一个类型,才可以正常调用
// 因为func是一个模板,要给T初始类型才能调用
??? func<int>();
}
int main()
{
test01();
??? test02();
??? return 0;
}
?
运行结果:
?
案例说明:
- 利用函数模板封装一个排序函数,可以对不同数据类型数组进行排序
- 排序规则从大到小,排序算法为选择排序
- 分别利用char数组和int数组进行测试
示例:
#include<iostream>
using namespace std;
?
// 交换的模板
template<typename T>
void func(T &a,T &b)
{
T temp =a;
a=b;
b=temp;
}
?
// 实现对数组的排序
template<typename T>
void Sort(T arr[],int len)
{
// 选择排序
for(int i=0;i<len;i++){
int max=i; // 认定第一个数就是最大值
for(int j=i+1;j<len;j++){
if(arr[max]<arr[j]){
max=j; // 更新最大值的下标
}
}
if(max!=i){
func(arr[max],arr[i]);
}
}
}
?
// 打印的模板
template<typename T>
void print_arr(T arr[],int len)
{
for(int i=0;i<len;i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
}
void test01()
{
char char_arr[] = "abcdefghijklmnopqrstuvwxyz";
int num = sizeof(char_arr) / sizeof(char);
cout << "排序前的顺序为:" << endl;
print_arr(char_arr, 26);
Sort(char_arr, 26);????????
cout << "从大到小排序后的顺序为:" << endl;
print_arr(char_arr, 26);
}
void test02()
{
int arr[] = { 7,8,3,4,6,5 };
int num = sizeof(arr) / sizeof(int);
cout << "排序前的顺序为:" << endl;
print_arr(arr, num);
Sort(arr, num);
cout << "从大到小排序后的顺序为:" << endl;
print_arr(arr, 6);
}
int main()
{
test01();
test02();
return 0;
}
运行结果:
?
普通函数和函数模板的区别:
- 普通类型函数调用时可以发生自动类型转换(隐式类型转换)
- 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
- 如果利用显示指定类型的方式,可以发生隐式类型转换
示例:
#include<iostream>
using namespace std;
?
//? 普通函数调用会发生隐式类型的转换
int add(int a,int b)
{
return a+b;
}
?
// 函数模板
template<typename T>
int my_ADD(T a,T b)
{
return a+b;
}
?
void test01()
{
int a=10;
int b=20;
char c='a'; // a=97
// 普通类型的函数会发生隐式类型转化
cout << "自动类型推导" << endl;
cout<<add(a,b)<<endl;
cout<<char(add(a,c))<<endl;
?
// 自动类型推导? 不会发生隐式类型转换
// 必须是相同的类型,一个int,一个char他不会计算
cout << "自动类型推导" << endl;
cout<<my_ADD(a,b)<<endl;
// 显示指定类型 会发生隐式类型转换
cout << "显示指定类型 会发生隐式类型转换" << endl;
cout << char(my_ADD<char>(a, c)) << endl;
cout<<my_ADD<int>(a,c)<<endl;
}
int main ()
{
test01();
?
return 0;
}
运行结果:
总结:建议使用显示指定类型的方式,调用函数模板,因为可以自己确定通用类型T
调用规则:
- 如果函数模板和普通函数都可以实现,优先调用普通函数
- 可以通过空模板参数列表强制调用函数模板
- 函数模板也可以重载
- 如果函数模板可以产生更好的匹配,优先调用函数模板
示例:
#include<iostream>
using namespace std;
void my_print(int a,int b)
{
cout<<"调用普通函数"<<endl;
}
template<typename T>
void my_print(T a,T b)
{
cout<<"模板函数调用"<<endl;
}
?
// 函模板重载
template<typename T>
void my_print(T a,T b,T c)
{
cout<<"重载--函数模板调用"<<endl;
}
void test01()
{
int a=10;
int b=20;
// 1. 如果函数模板和普通函数都可以实现,优先调用普通函数
// 即使普通函数只有定义没有实现,只会报错,不会调用模板函数
my_print(a,b);
my_print(a,b);
?
// 2. 通过空模板参数列表,强制调用函数模板
cout<<"通过空模板参数列表,强制调用函数模板"<<endl;
my_print<>(a,b);
?
// 3. 重载函数模板调用
???? my_print(a,b,100);
?
???? // 4.如果函数模板可以产生更好的匹配,优先调用函数模板
???? char c1 = 'a';
???? char c2 = 'b';
cout << "更好的匹配" << endl;
???? my_print(c1,c2);
?}
?int main()
?{
test01();
return 0;
?}
运行结果:
模板并不是万能的,有些特定数据类型,需要用具体化方式实现。
利用具体化的模板可以实现自定义类型的通用化
学习模板并不是为了写模板,而是在STL能够运用系统提供的模板
不支持数组之间的赋值操作
Template<Typename T>
void f(T a, T b)
{
a=b;
}
?
不支持自定义类型
提供重载函数的方式,可以为特定类型提供具体的模板
Template<typename T>
void f(T a,T b)
{
if(a>b)
...
}
解决方法
示例:
#include<iostream>
using namespace std;
class person
{
public:
person(string name,int age)
{
this->name=name;
this->age=age;
}
string name;
int age;
};
// 对比两个数据是否相等
template<typename T>
bool my_compare(T &a,T &b)
{
if(a==b)
return true;
else
return false;
}
?
// 利用具体化的对象实现,具体优化先调用
template<> bool my_compare(person &p1,person &p2)
{
if(p1.name==p2.name&&p1.age==p2.age)
return true;
else
return false;
}
?
void test01()
{
int a = 10;
int b =20;
bool ret = my_compare(a,b);
if(ret)
cout<<"a==b"<<endl;
else
cout<<"a!=b"<<endl;
}
// person数据类型模板不认识
void test02()
{
person p1("Tom",18);
person p2("Jack",20);
bool ret = my_compare(p1,p2);
if(ret)
cout<<"p1==p2"<<endl;
else
cout<<"p1!=p2"<<endl;
}
int main()
{
test01();
test02();
return 0;
}
运行结果:
?