目录
一、泛型编程
泛型编程的概念:
泛型编程举例:
二、函数模板
函数模板的概念:
函数模板的格式:
函数模板的实例化:
隐式实例化:
显式实例化:
模板参数的匹配原则:
三、类模板
类模板的格式定义:
类模板的举例:
类模板的实例化:
当我们在编写程序时可能会遇见这样的问题:交换函数是使用频率非常高的函数,当我们想在一个工作台下同时交换char、int、double类型的数据时,我们要怎么做呢?
在C语言中,我们只能通过不同的函数名,对数据类型不同、函数功能相同的函数进行区分。
void Swapi(int left, int right) {//此处省略函数体}
void Swapd(double left, double right) {//此处省略函数体}
void Swapc(char left, char right){//此处省略函数体}
……
由于C++引入了函数重载的概念,使用相同的函数名,编译器可以根据形参的数据类型不同进行区分。
void Swap(int& left, int& right) {//此处省略函数体}
void Swap(double& left, double& right) {//此处省略函数体}
void Swap(char& left, char& right){//此处省略函数体}
……
使用函数重载虽然可以实现,但有几个不好的地方:
- 函数从在仅仅时类型不同,代码复用率低,有新类型出现时需要用户自己增加对应函数
- 代码可维护性低,当代码出错时,可能需要对所有的代码都需要修改
仔细观察会发现,上述代码只有数据类型不同,其他代码实现都相同。那么是否可以将代码作为模具,将数据类型设为X(未知数据类型),在具体调用时,再将实际的数据类型和X进行替换,就可以实现一份代码,解决函数重载带来的所有问题。因此,引入了泛型编程的思想。
一、泛型编程
泛型编程的概念:
泛型编程是一种编程范式,它的目的是提高代码的重用性和灵活性。泛型编程允许程序员使用不同的数据类型来实现相同的算法和数据结构,从而编写更加通用和可扩展的代码。
泛型编程举例:
实现一个通用的交换函数
//template模板关键字,typename模板中数据类型不是固定的,需要编译器自动推导
template<typename T>
void Swap(T& left, T& right)
{T temp = left;left = right;right = temp;
}int main()
{int a = 10, b = 20;double c = 10.12, d = 20.34;Swap(a, b);Swap(c, d);cout << "a:" << a << " " << "b:" << b << endl;cout << "c:" << c << " " << "d:" << d << endl;return 0;
}
二、函数模板
函数模板的概念:
函数模板是一种通用的函数定义形式,允许定义一个接受任意类型参数的函数。它的定义形式类似于一个普通函数,但是使用了类型占位符(通常为T)来代替具体类型。在函数调用时,编译器会根据调用时传递的参数类型来确定T的具体值并生成相应的函数实例。
函数模板的格式:
template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表) {}
template<typename T>//typename是用来定义模板参数关键字,也可以使用class
void Swap(T& left, T& right)
{T temp = left;left = right;right = temp;
}
函数模板的实例化:
函数模板并不是实际存在的,只有当系统调用时,才会根据传递过去的实际类型进行实例化。分为隐式实例化和显式实例化
隐式实例化:
编译器根据实参,推演模板参数的实际类型
template<class T>
T Add(const T& left, const T& right) { return left + right; }
int main()
{int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;Add(a1, a2);//实参是int,编译器将T替换成int,实例化出具体函数Add(d1, d2);//Add(a1, d1);//注意:在模板中,编译器一般不会进行类型转换操作,实参是int和double两种类型,编译器会报错//想解决这种问题有两种方式:1、类型转换;2、显式实例化Add(a1, (int)d1);return 0;
}
显式实例化:
在函数名后,用<>指定模板参数的实际类型
template<class T>
T Add(const T& left, const T& right) { return left + right; }
int main()
{int a = 10;double b = 20.4;// 显式实例化Add<int>(a, b);//如果实参类型和指定的类型不匹配,会进行类型转换,失败会报错return 0;
}
模板参数的匹配原则:
一个非模板参数和一个同名的函数模板同时存在,如果其他条件都相同,首选非模板参数
//非模板函数
int Add(const int& left, const int& right) { return left + right; }//模板函数
template<class T>
T Add(const T& left, const T& right) { return left + right; }int main()
{Add(1, 2);//首选非模板函数Add<int>(1, 2);//显示实例化,会选择模板函数return 0;
}
一个非模板函数和一个同名的函数模板同时存在,函数模板生成的函数更加匹配,会选择函数模板
//非模板函数
int Add(const int& left, const int& right) { return left + right; }//模板函数
template<class T1,class T2>
T1 Add(const T1& left, const T2& right) { return left + right; }int main()
{Add(1, 2);//与非模板函数参数类型相同,首选非模板函数Add(1, 2.0);//与模板函数生成的函数更匹配,选择模板函数return 0;
}
函数模板不能进行隐式类型转换,非模板函数可以进行隐式类型转换
三、类模板
类模板的格式定义:
template<class T1, class T2, ..., class Tn>
class 类模板名
{// 类内成员定义
};
类模板的举例:
//模拟vector
//vextor不是一个具体的类,编译器会根据实例化的具体类型从而生成具体类的容器
template<class T>
class vector
{
public:vector(size_t capacity = 10):_pData(new T[capacity]),_size(0),_capacity(capacity){ }//……以下细节省略
private:T* _pData;size_t _size;size_t _capacity;
};
类模板的实例化:
template<class T>
class vector
{
public:vector(size_t capacity = 10):_pData(new T[capacity]),_size(0),_capacity(capacity){ }//……以下细节省略
private:T* _pData;size_t _size;size_t _capacity;
};int main()
{//使用显式实例化指定类型vector<int> v1;return 0;
}