C++另一种编程思想 泛型编程 主要利用的技术就是模板
提供两种模板机制 : 函数模板 和 类模板
模板
模板就是建立通用的模具 , 大大提高复用性。
特点:模板不可直接使用 它只是一个框架 模板的通用不是万能的。
函数模板
函数模板语法 建立通用函数,其函数返回值类型和形参类型不可以具体制定,用一个虚拟类型代表。
语法 template<typename T> 函数声明或定义
template声明创建模板 typename 表示其后面的符号是一种数据类型 可以用class 代替,老师建议都是用class。T 通用的数据类型 名称可以替换 通常为大写字母。
下面使用函数模板来实现交换两个数字或者字符等不同的类型
#include<iostream>
#include<string>
using namespace std;
template<typename T>
void myswapInt(T &a , T &b)
{
T temp = a;
a = b ;
b = temp;
}
void test01()
{
int a = 10 ;
int b = 10;
myswapInt(a , b); //第一种调用模板方法,自动类型推导
myswapInt<int>(a , b);//第二种调用方法,显示指定类型
}
int main()
{
test01();
system("pause");
system("cls");
}
前面还学了一个新的排序方式: 选择排序,这个需要好好理解一下,自己试一试敲以下代码
#include<iostream>
#include<string>
using namespace std ;
//创建交换数字的模板
template<typename T>
void myswap(T &a , T&b)
{
T temp = a ;
a = b ;
b = temp;
}
template<typename T>
void mySort(T arr[] , int len)
{
for (int i = 0 ; i < len ; i++)
{
int max = i;
for (int j = i+1 ; j < len ; j++)
{
if (arr[j] > arr[max])
{
myswap(j , max ) ;//交换下标
}
}
if(max != i)
{
myswap(arr[max] , arr[i]);
}
}
}
//打印数组的 函数模板
template<typename T>
void printinfo( T arr[] , int len)
{
for (int i = 0 ; i < len ; i++)
{
cout << arr[i] ;
}
cout << endl;
}
//实现字母排序,首先有一个chararr[] 从大到小
void test01()
{
char chararr[] = "badecf";
int len = sizeof(chararr)/sizeof(chararr[0]);
mySort(chararr , len);
printinfo(chararr , len);
}
int main()
{
test01();
system("pause");
return 0 ;
}
想想这个选择排序的一个过程, 首先在外层循环的时候,先记第一个元素的下标的值为最大值,然后进入内层循环,找arr[i+1]的值,并且判断arr[i+1]值是否会大于之前选定的arr[max]值,如果大于,则arr[i+1]为当前最大值 , 需要交换一下max 和 i 的值,然后继续判断循环,这一轮循环结束后就找到了最大值 ,出循环后,需要判断最大值的下标索引max是否和下标索引为i 的值相等,如果不相等,就交换arr[max]和arr[i]的值,那么i位置上就是最大值,i 目前为 0 ,这就是从大到小进行排序。
普通函数与函数模板区别
1、普通函数调用时可发生自动类型转换(隐式类型转换)
2、函数模板调用时,如果利用自动类型推导,不会发生隐式
int myadd01(int a , int b )
{
return a+b ;
}
template<class T>
int myadd(T a , T b )
{
return a + b ;
}
void test01()
{
int a = 10 ;
int b = 20 ;
char c = 'c';
myadd(a , c) ; // 报错 , 会显示数据类型不一样
myadd01(a , c) ; 不报错,因为普通函数会发生隐式数据类型转换 会将字符型c转换为int型
cout << myadd01(a , c) << endl; //输出109 c - 99
//如果要使用函数模板必须要指定类型方式 , 因为使用自动类型推导函数模板不会发生隐式类型转换
myadd<int>(a , c ); // 不报错,使用显示指定类型方法
cout << myadd(a , c) << endl;
}
总结:建议使用显示指定类型的方式调用函数模板 , 因为可以自己确定通用类型T
普通函数与函数模板调用规则
1、如果函数模板和普通函数都可以实现 , 优先调用普通函数
2、可以通过空模板参数列表来强制调用函数模板
3、函数模板也可以发生重载
4、如果函数模板可以产生更好的匹配,优先调用函数模板
void Myprint(int a, int b) // 优先找函数模板,但是只有声明没有实现,所以会报错
{
cout << "调用的普通函数" << endl;
}
template<typename T>
void Myprint(T a, T b)
{
cout << "调用的函数模板" << endl;
}
template<typename T>
void Myprint(T a, T b , T c)
{
cout << "调用重载的函数模板" << endl;
}
void test01()
{
int a = 10;
int b = 20;
//Myprint(a, b);
//Myprint<>(a , b);//通过空模板参数列表,强制调用函数模板
//Myprint<>(a, b, 100);
//4、如果函数模板可以产生更好的匹配,优先调用函数模板
char c1 = 'a';
char c2 = 'b';
Myprint(c1, c2);
}
总结:既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性
函数模板局限性
模板的通用性并不是万能的,为了解决自定义数据类型的模板 , C++提供了模板重载,可以为特定的类型提供具体化模板。
class Person
{
public:
Person( string name , int age)
{
this->m_name = name ;
this->m_age = age ;
}
string m_name;
int m_age ;
};
//对比两个数相等的函数模板
template<class T>
bool mycompare(T a , T b )
{
if ( a == b )
{
return true;
}
else
{
return false;
}
}
//所以要自定义数据类型的模板才可以使用
template<> bool mycompare(Person&p1 , Person&p2 )
{
if(p1.m_name == p2.m_name && p1.m_age == p2.m_age)
{
return true;
}
else
{
return false;
}
}
//上述定义的函数模板不能用在自定义的函数类型,用于自定义函数类型会报错
void test01()
{
Person p1("Tom" , 10);
Person p2("Tom" , 10);
bool ref = mycompare(p1, p2);// 报错,因为是自定义的数据类型,编译器不知道如何比较
//当定义完自定义数据类型模板之后便可以使用模板
bool ref = mycompare(p1, p2);
if (ref)
{
cout << "p1 == p2" << endl;
}
else
{
cout << "p1 != p2" << endl;
}
}
总结 利用具体化模板可以解决自定义类型的通用化
学习模板并不是为了写模板 而是在STL能够运用系统提供的模板.