目录
1.泛型编程
void swap(char& p1, char& p2)
{
int temp = p1;
p1 = p2;
p2 = temp;
}
void swap(int& p1, int& p2)
{
int temp = p1;
p1 = p2;
p2 = temp;
}
void swap(double& p1, double& p2)
{
int temp = p1;
p1 = p2;
p2 = temp;
}
看以上几个swap函数,对于不同类型变量的值交换,我们就需要写不同的swap函数实现这一功能,虽然对于函数重载来说,这并不是什么难事,但是,我们能明显发现使用重载函数应对这种情况是有很大缺点的:
1.使用重载的函数仅仅是类型不同,这就会导致代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数;2.此外,对于代码的可维护性也将降低,一个小的失误就可能导致所有的重载函数失效。
2.函数模板
2.1概念
2.2函数模版使用格式
函数模版的使用是由关键字template所定义的。其使用格式为:
- template<typename/class 参数1, typename 参数2,......,typename 参数n>
- 返回值类型 函数名(参数列表){}
其中,关键字typename和class都可使用用于定义模版参数,此外模版参数的个数也是可以按需添加的,各个参数之间使用逗号隔开,可以类比函数参数的使用。此外还需要注意的是,一个template所定义的模版只能用于一个函数,不能同时用于多个使用。
使用模版函数的swap函数:
template<class T>
void sawp(T& p1, T& p2)
{
int temp = p1;
p1 = p2;
p2 = temp;
}
2.3函数模版原理
在调用模版函数时,编译器会根据函数中传递实参的类型,推演生成带对应参数的函数。
2.4函数模版的实例化
2.41概念
2.42隐式实例化
函数模版的隐式实例化是我们前面函数模版原理中所提到的用法,隐式实例化是通过函数模版调用时所传递的实参类型来进行实例化的,根据不同的实参类型,编译器会生成对应不同的函数版本。
template <class T>
T Add(T x, T y)
{
return x + y;
}
以上面简单的函数模版为例,当我们传递的实参为int型时,编译器会生成一个参数为int型的函数供用户使用,同理,当传递实参为double型时,也会生成对应参数为double类型的函数。
注意:隐式实例化时,传递实参不能够出现其传递类型的参数类型个数大于模版函数参数类型个数,这会导致参数无法匹配,使得隐式实例化失败。
2.43显示实例化
显示实例化是通过用户自己指定函数模版的参数类型来实现的,显示实例化的方法是:
在函数名后的<>中指定模板参数的实际类型
依然以模版函数Add为例:
template <class T>
T Add(T x, T y)
{
return x + y;
}
通过用户自己手动指定模版函数参数类型,即使出现实参中不匹配的情况,作为浮点型的3.14在作为实参传递给函数形参时,也会被视为是int型,强转成值为3的int型,最后得到函数返回值ret2 为5的结果。
2.5模板参数的匹配原则
1.一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数且不会发生了冲突。
int Add(int x, int y)
{
return x + y;
}
template <class T>
T Add(T x, T y)
{
return x + y;
}
int main()
{
Add(1, 2);
Add<int>(1, 2);//实例化函数模版参数类型为int
return 0;
}
2.对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例;如果模板可以产生一个具有更好匹配的函数,那么将选择实例化一个更合适的函数。
int Add(int x, int y)
{
return x + y;
}
template <class T>
T Add(T x, T y)
{
return x + y;
}
int main()
{
//寻找到参数完全匹配的函数,使用存在的Add函数
Add(1, 2);//有现成合适的使用现成的
//1.未发现有合适匹配参数的Add函数
//2.隐式实例化生成合适的Add函数,使用实例化生成的函数
Add(1.12, 3.14);//没有现成的合适的,自己生成
//1.未发现有合适匹配参数的Add函数
//2.尝试隐式实例化生成更合适的函数,模版参数不匹配,生成失败
//3.继续使用参数不太匹配的Add函数
Add(1, 3.14);//没有现成合适的,但生成不了,将就使用不太合适的
return 0;
}
3.模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
前面我们也提到过,隐式实例化的过程不能出现实参与模版参数匹配错误的情况,则会导致实例化失败,而普通的函数,当实参与参数产生冲突时,会优先尝试隐式的类型转换,匹配参数,只有无法进行自动类型转换的情况,编译器才会报错。
3.类模版
类模版与函数模版在大致上是一致的,但也有些许差异,这里概念就不重复赘述了。
3.1类模版的使用格式
template<typename/class 参数1 , class 参数2, ..., class 参数n>class类模板名{};
类模版使用的格式与函数模版没有太大区分,主要区分在于函数和类两者之间的声明定义。
template <class T>
class A
{
private:
T _a;
public:
A(T a)
:_a(a)
{}
};
需要注意的点:类中的函数如果是类中声明,类之外定义的情况
定义的函数需要指明类模版参数列表,所以还要多使用一个模版,使用类名<>的形式,在<>中写上模版参数。
template <class T>
class A
{
private:
T _a;
public:
A(T a);
};
template <class T>
A<T>::A(T a)//类中声明,类外定义
:_a(a)
{}
3.2类模版的实例化
类的实例化与函数模版的实例化不同,类的实例化只能够显示实例化,这是因为类的实例化没有所谓实参的传递,编译器并不能通过实参去推演出模版参数的类型。
template <class T>
class A
{
private:
T _a;
public:
A(T a)
:_a(a)
{}
};
int main()
{
A<int> a1(1);
A<double> a2(3.14);
return 0;
}
显示示例化后的两个类对象是两个不同类的对象,不属于同一个类。
结语
这节对于C++中模版的初阶知识介绍到这里就结束了,下期将继续带来C++相关的后续知识,感谢各位的观看,有错误或者需要改进的地方还请指出,如果有帮助的话,还请点个赞呀!