1.模版
- 模版不可以直接使用,它只是一个框架
- 模版并不是万能的
- C++另一种编程思想称为泛型编程,主要利用的技术就是模版
- C++提供两种模版机制:函数模版 和类模版
2.函数模版
- 作用:建立一个通用函数,函数返回值类型 和形参类型可以不具体指定,用一个虚拟的类型来表示。
- 语法:template(typename T)
- 解释:template---声明创建模版,typename---表明其后面的符号是一种数据类型,可以用class代替,T---通用的数据类型,名称可以替换,通常为大写字母
cpp
void swapInt(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
void swapFloat(float &a, float &b)
{
float temp = a;
a = b;
b = temp;
}
// 创建模版
template<typename T> // 声明一个模版,且T是一个通用的数据类型
void mySwap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
void test01()
{
int a = 10;
int b = 20;
// swapInt(a,b);
// 两种方式使用函数模版
// 1.自动类型推导
mySwap(a, b);
// 2.显示指定类型
mySwap<int>(a, b);
cout << a << endl;
cout << b << endl;
}
1.函数模版利用关键字template
2.使用函数模版有两种方式:自动类型推导、显示指定类型
3.模版的目的是为了提高复用性,将类型参数化
3.函数模版注意事项
- 自动类型推导,必须推导出一致的数据类型T,才可以使用
- 模版必须确定出T的数据类型,才可以使用
cpp
// 创建模版
template<class T> // 声明一个模版,且T是一个通用的数据类型
void mySwap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
void test01()
{
int a = 10;
int b = 20;
char c ='c';
// mySwap(a, c); // 错误,推导不出一致的T类型
}
template<class T>
void fun()
{
cout << "func调用" << endl;
}
void test02()
{
//fun(); // 错误,模版必须确定出T的数据类型,才可以使用
fun<int>();
}
4.函数模版案例
- 利用函数模版封装一个排序的函数,可以对不同数据类型数组进行排序
- 排序规则从大到小,排序算法为选择排序
- 分别利用char数组和int数组进行测试
cpp
template<class T>
void Swap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
// 对数组进行排序
template<class T> // 声明一个模版,且T是一个通用的数据类型
void Sort(T array[], int len)
{
for(int i=0; i<len; i++)
{
int max = i;
for (int j=i; j<len ; j++)
{
if(array[max]<array[j])
{
max = j;
}
}
if(max != i)
{
Swap(array[i], array[max]); // ????
}
}
}
template<class T>
void printArray(T array[], int len)
{
for(int i=0; i<len; i++)
{
cout << array[i] << " ";
}
cout << endl;
}
void test01()
{
char charArray[] = "badcfe";
int len = sizeof(charArray) / sizeof(char);
Sort<char>(charArray, len);
printArray<char>(charArray, len);
}
void test02()
{
int intArray[] = {7,5,8,6,3,4};
int len = sizeof(intArray) / sizeof(int);
Sort<int>(intArray, len);
printArray<int>(intArray, len);
}
5.普通函数和函数模版的区别
- 普通函数调用时可以发生隐式类型转换
- 函数模版调用时,如果利用自动类型推导,不会发生隐式类型转换
- 如果利用显示指定类型的方式,可以发生隐式类型转换
cpp
int Add01(int a, int b)
{
return a + b;
}
template<class T>
T Add02(T a, T b)
{
return a + b;
}
void test01()
{
int a = 10;
int b = 20;
char c = 'c';
cout << Add01(a,c) << endl; // 普通函数里,字符型'c'可以隐式转换为整型,ASCII码为99
// 自动类型推导
cout << Add02(a,c) << endl; // 错误,函数模版调用时,如果利用自动类型推导,不会发生隐式类型转换
// 显示指定类型
cout << Add02<int>(a,c) << endl; // 正确,利用显示指定类型的方式,可以发生隐式类型转换
}
建议使用显示指定类型的方式,调用函数模版,因为自己可以确定通用类型T
6.普通函数和函数模版的调用规则
- 如果函数模板和普通函数都可以实现,优先调用普通函数
- 可以通过空模板参数列表来强制调用函数模板
- 函数模板也可以发生重载
- 如果函数模板可以产生更好的匹配,优先调用函数模板
cpp
void Print(int a, int b)
{
cout << "调用的是普通函数" << endl;
}
template<class T>
void Print(T a, T b)
{
cout << "调用的是函数模版" << endl;
}
template<class T>
void Print(T a, T b, T c)
{
cout << "调用的是重载函数模版" << endl;
}
void test01()
{
int a = 10;
int b = 20;
Print(a, b); // 1.函数模板和普通函数都可以实现时,优先调用普通函数
// 2.通过 空模板 参数列表来强制调用函数模板
Print<>(a, b);
// 3.函数模板也可以发生重载
Print(a, b, 100); // 重载函数模版
// 4.如果函数模板可以产生更好的匹配,优先调用函数模板
char c1 = 'a';
char c2 = 'b';
Print(c1, c2); // 函数模版
}
7.模版的局限性
- 模版并不是通用的(对自定义数据类型无效)
- 为了解决这种问题,提供模版的重载,C++可以为这些特定的类型 提供具体化的模版
cpp
class Person
{
public:
Person(string name, int age)
{
m_name = name;
m_Age = age;
}
int m_Age;
string m_name;
};
// 判断两个数据是否相等
template<class T>
bool Compare(T &a, T &b)
{
if (a == b)
{
return true;
}
else
{
return false;
}
}
// 利用具体化Person的版本来实现代码,这种具体化会优先调用
template<> bool Compare(Person &p1, Person &p2)
{
if (p1.m_name == p2.m_name && p1.m_Age == p2.m_Age)
{
return true;
}
else
{
return false;
}
}
void test01()
{
int a = 10;
int b = 20;
bool res = Compare(a, b);
cout << res << endl;
}
void test02()
{
Person p1("Tom", 10);
Person p2("Tom", 10);
bool res = Compare(p1, p2); // 走具体化的代码
cout << res << endl;
}
利用具体化的模版,可以解决自定义类型的通用化
学习模版并不是为了写模版,而是在STL能够运用系统提供的模版