1.泛型编程
泛型编程是一种编程范式,允许编写与类型无关的代码。通过使用泛型,可以编写可以与多种数据类型一起使用的代码,增加代码的重用性和灵活性。例如,在编写一个排序算法时,泛型使得同一个排序函数可以处理整数、浮点数或自定义对象等不同类型的数据。
/* 实现数值交换 */
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
void Swap(double& left, double& right)
{
int temp = left;
left = right;
right = temp;
}
void Swap(char& left, char& right)
{
int temp = left;
left = right;
right = temp;
}
/*
虽然我们可以使用函数重载实现
但是代码利用率低,只要有新的类型出现
用户就必须手动实现,增加对应的函数
代码维护性较低,一个出错很可能其他重载都会出错
*/
//......
2.函数模版
函数模版代表了一个函数家族,该函数模版与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
格式 :template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表){}
/* typename是用来定义模版参数关键字,也可以使用class(不可使用struct) */
template <typename T>
void Swap(T& left, T& right)
{
T temp = left;
left = right;
right = temp;
std::cout << "left[" << left << "]right[" << right << "]" << std::endl;
}
int main()
{
int left = 0, right = 10;
/*
使用方法1 函数名<类型>(参数)
2:可直接按照调动普通函数的方式调用
*/
Swap<int>(left, right);
Swap(left, right);
return 0;
}
函数模版不是函数,是编译器使用方法产生特定具有类型函数的模具
在编译器编译阶段,对于模版函数的,编译器会根据传入的实参类型进行类型推演,生成对应的类型以供调用。
函数模版的实例化
用不同类型的参数使用函数模版时,成为函数模版的实例化。
模版参数实例化(隐式实例化和显示实例化)
/* typename是用来定义模版参数关键字,也可以使用class(不可使用struct) */
template <typename T>
void Swap(T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
int main()
{
/*
隐式类型实例化
*/
int left = 0, right = 10;
Swap(left, right);
double left1 = 2.1;
double right1 = 10.3;
Swap(left1, right1);
/*
错误写法:Swap(left, right1)
此时编译器看到此实例化时,进行类型推演
left推演出来的是int
right1推演出来的是double
但是参数模版中只有一个T
此时隐式调用编译器无法确定该T为什么类型
注:编译器一般不进行类型转换操作
此时我们应该选用显示类型实例化
*/
/* 显示类型实例化 */
char left2 = 2, right2 = 2;
Swap<char>(left2, right2);
return 0;
}
注:隐式类型实例化,类型不匹配,则无法转换成功,编译器将报错
模版参数调用匹配原则
/*
当非模版函数和函数模版同名且同时存在时,
函数模版依旧可以实例化为该类型
*/
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
std::cout << "void Swap(int& left, int& right)" << std::endl;
}
template <typename T>
void Swap(T& left, T& right)
{
T temp = left;
left = right;
right = temp;
std::cout << "void Swap(T& left, T& right)" << std::endl;
}
int main()
{
int Ileft = 10, Iright = 20;
/* 与非模版函数匹配, 编译器不需要特化 */
Swap(Ileft, Iright);
/* 编译器需要特化为Swap版本 */
Swap<int>(Ileft, Iright);
return 0;
}
int ADD(int left, int right)
{
std::cout << "int ADD(int& left, int& right)" << std::endl;
return left + right;
}
template <typename T1, typename T2>
int ADD(T1 left, T2 right)
{
std::cout << "int ADD(T1& left, T2& right)" << std::endl;
return left + right;
}
int main()
{
/*
对于隐式类型实例化
非模版函数和同名函数模版,
如果其他条件相同,编译器优先调用非模版函数
如果模版可以产生一个具有更好的匹配函数,则使用模版
*/
ADD(1, 2);/* 与非模版函数匹配 */
ADD(1, 2.0);/* 模版函数可以产生更好的匹配函数 */
}
注: 模版函数不允许自动类型转换, 但普通函数可以进行自动类型转换
3.类模版
类模版的格式定义
template <typename T, typename U ......>
class ClassName {
// 类定义
};
/*
* #define type int
之前使用define定义存储类型
无法在一个类中,存储不同类型
注意:Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具
*/
template <class T>
class Vector
{
public:
Vector(size_t capacity = 10)
: _pData(new T[capacity])
, _size(0)
, _capacity(capacity)
{}
~Vector();
void PushBack(const T& data);
void PopBack();
// ...
size_t Size() { return _size; }
T& operator[](size_t pos)
private:
T* _pDate;
size_t _size;
size_t _capacity;
};
/* 注:类模板中函数放在类外进行定义时,需要加模板参数列表 */
template <class T>
Vector<T>::~Vector()
{
if (_pData)
delete[] _pData;
_size = _capacity = 0;
}
类模版的实例化与函数模版实例化不同,类模版实例化需要再类模版后面跟<>, 将需要实例化的类型放入<>中
注: 类模版类名不是真正的类, 而实例化后的结果才是类。
/* Vector类名 Vector<int>才是类型 */
Vector<int> s1;
Vector<double> s1;