提高阶段针对C++泛型编程和STL技术探讨深层次使用
目录
一、模板概念
建立通用的模具,提高复用性;C++的另一种编程思想称为泛型编程,主要利用的技术是模板
学习模板可以更好的运用后续STL系统提供的模板
二、函数特点
①不可以直接使用
②模板的通用不是万能的
三、函数模板
不具体指定函数返回值和形参的类型,用虚拟的类型代表:T function(T a)
目的:提高代码复用性,将类型参数化
语法:
template<typename T>
//函数声明或定义
template -- 声明创建模板
typename -- 表明后面的符号是一个数据类型,可以用class代替
T -- 通用数据类型,名称可以用其他大写字母代替
案例引入:
//交换两个整型的函数
void swapInt(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
//交换两个浮点型的函数
void swapDouble(double &a, double &b)
{
double temp = a;
a = b;
b = temp;
}
观察以上两个函数的不同之处仅为形参的类型,故可以利用一个函数可传入多种数据类型简化代码
template<typename T> //声明一个模板告诉编译器后面代码中紧跟的T不要报错,T是通用数据类型,在使用时才会被指定
void Swap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
//使用模板函数
//方法1:自动类型推导
int a = 10;
int b = 20;
Swap(a,b);
//方法2:显示指定类型,函数后跟着的<>内容是指定上述模板函数的T类型
Swap<int>(a,b);
注意事项:
①使用自动类型推导时,必须推导出一致的数据类型T才可以使用(形参的数据类型一致)
②模板必须要确定出T的数据类型才能使用(如果该模板函数形参是空白,需要用显示指定类型随便写一个类型)
案例实操-对不同数据类型的数组进行选择排序(从大到小),测试组 int 和 char 类型:
template<typename T>
void Swap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
template<class T>
void change(T &a[],int len)
{
for(int i = 0; i<len; i++)
{
int max = i;
for(int j=i+1; j<len; j++)
{
if (a[max]<a[j+1])
{
max = j;
}
}
if(max != i)
{
swap(a[max],a[i]);
}
}
}
template<typename T>
void printArr(T arr[],int len)
{
for(int i=0; i<len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
void test01()
{
char charArr[] = "badcfe";
int num = sizeof(charArr) / sizeof(char);
change(charArr,num);
printArr(charArr,num)
}
void test02()
{
int intArr[] = {7,5,1,3,9,2,4,6,8};
int num = sizeof(intArr) / sizeof(int);
change(intArr,num);
printArr(intArr,num)
}
四、普通函数和函数模板的区别
①普通函数在使用时可以发生自动类型转换 [隐式类型转换:将double类型赋给int]
int add(int a, int b)
{
return a+b;
}
void test01()
{
int a = 10;
int b = 20;
char c = 'c';
//可以运行
add(a,b);
//可以运行,将字符变量转化为ascii的值99赋值给int类型
add(a,c);
}
②函数模板使用时,如果利用自动类型推导不会发生隐式类型转换;如果利用显示执行类型可以发生隐式类型转换
template<typename T>
T add(T a, T b)
{
return a + b;
}
void test()
{
int a = 10;
int b = 20;
char c = 'c';
//可以运行
add(a,b);
//自动类型推导不可以运行,无法推导出T的类型
add(a,c);
//显示指定类型可以运行
add<int>(a,c);
}
利用模板函数时,建议使用显示指定类型方式调用模板函数,因为自己可以确定T类型
五、普通函数和模板函数的调用规则
①如果函数模板和普通函数都可以实现,优先调用普通函数
②可以通过空模板参数列表强制调用模板函数:print<>(a,b);
③函数模板可以发生重载
④如果函数目标你可以产生更好的匹配,优先调用函数模板
如果有模板函数,就尽量不要再写重名的普通函数,避免出现二义性
六、模板的局限性
模板的通用性不是万能的
template<typename T>
void f(T a, T b)
{
a = b;
}
以上模板如果a和b是一个数组的话,赋值操作就无法实现
template<typename T>
void f(T a, T b)
{
if(a > b){....}
}
以上模板如果自定义类型T是Person类的自定义数据类型,则无法正常运行
为解决以上问题,C++提供模板的重载
class Person
{
public:
Person(string name, int age)
{
this.m_Name = name;
this.m_Age = age;
}
string m_Name;
int m_Age;
};
template<typename T>
bool Com(T &a, T &b)
{
if(a == b)
{
return true;
}
else
{
return false;
}
}
template<> bool Com(Person &p1, Person &p2)
{
if(p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age)
{
return true;
}
else
{
return false;
}
}
void test()
{
Person p1("TOM",10);
Person p2("TOM",10);
bool ret = Com(p1,p2);
if(ret)
{
cout << "p1 == p2" << endl;
}
else
{
cout << "p1 != p2" << endl;
}
}
注意:template<>是显示具体化的原型,即通过名称指出类型,优先于常规模板