模板(C++)

1.概述

问题:怎么理解C++里边的多态?
静态的多态(编译时期的多态):函数重载,模板 。
动态的多态(运行时期的多态):继承中的虚函数。

(1)模板是C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。
(2)模板是一种对类型进行参数化的工具;模板通常有两种形式:函数模板和类模板函数模板针对仅参数类型不同的函数;类模板针对仅数据成员和成员函数类型不同的类
(3)使用模板的目的就是能够让程序员编写与类型无关的代码。比如编写了一个交换两个整型int 类型的swap函数,这个函数就只能实现int 型,对double,字符这些类型无法实现,要实现这些类型的交换就要重新编写另一个swap函数。使用模板的目的就是要让这程序的实现与类型无关,比如一个swap模板函数,即可以实现int 型,又可以实现double型的交换。模板可以应用于函数和类。
注意:模板的声明或定义只能在全局,命名空间或类范围内进行。也就是说不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。

2.什么是模板??

(1)模板是C++的一种特性,允许函数或类(对象)通过泛型(generic types)的形式表现或运行。
(2)模板可以使得函数或类在对应不同的型别(types)的时候正常工作,而无需为每一个型别都写一份代码。
(3)模板是一种泛型编程的机制,也是一种复用的手段
(4)模板是通用语言的特性,模板又叫参数化类型(parametrized types)。

3.什么是函数模板??

函数模板,实际上是建立一个通用函数,它所用到的数据的类型(包括返回值类型、形参类型、局部变量类型)可以不具体指定,而是用一个虚拟的类型来代替(实际上是用一个标识符来占位),**等发生函数调用时再根据传入的实参来逆推出真正的类型。**这个通用函数就称为函数模板(Function Template)。

4.什么是类模板??

C++ 除了支持函数模板,还支持类模板(Class Template)。函数模板中定义的类型参数可以用在函数声明和函数定义中,类模板中定义的类型参数可以用在类声明和类实现中。类模板的目的同样是将数据的类型参数化。

5.模板的实例化

编译器调用模板函数时,编译器会根据实参的类型,推演出模板的类型,并再生产相应的代码,称为模板的实参推演

B<int> a; //类模板只能显式实例化,没法推演 
fun<int>(2,6); //函数模板的显式实例化,不需要推演
fun(2,6) //函数模板的隐式实例化,需要进行推演

6.模板的参数列表

模板的参数列表有两种:模板类型参数、模板非类型参数

(1)模板类型参数
类型参数由关键字class和typename后接说明符构成。
例如:

template<class T>
void fun(T a){};

其中T就是一个类型形参,类型形参的名字由用户自已确定。模板形参表示的是一个未知的类型。模板类型形参可作为类型说明符用在模板中的任何地方,与内置类型说明符或类类型说明符的使用方式完全相同,也就是说可以用于指定返回类型,变量声明等。

不能为同一个模板类型形参指定两种不同的类型,比如

template<class T>
void fun(T a, T b){}// fun(25,12.2)  == > 错的!!!

对函数模板来讲:
语句调用fun(25, 12.2)将出错,因为该语句给同一模板形参T指定了两种类型,第一个实参25把模板形参T指定为int,而第二个实参12.2把模板形参指定为double,两种类型的形参不一致,会出错。

对类模板来讲:

template<class T>
T fun(T a, T b){}

A<int> a	// 实例化模板类对象为a
fun(25,12.2)

语句调用fun(25,12.2)在编译时不会出错,但会有警告,因为在声明类对象的时候已经将T转换为int类型,而第二个实参12.2把模板形参指定为double,在运行时,会对12.2进行强制类型转换为12。 当我们声明类的对象为:A a,此时就不会有上述的警告,因为从int到double是自动类型转换。

(2)模板的非类型参数
模板的非类型参数也就是内置类型参数,如

template<class T, int a> 

其中int a就是非类型的模板参数。

非类型形参在模板定义的内部是常量值,也就是说非类型形参在模板的内部是常量。

非类型模板的形参只能是整型,指针和引用,像double,String, String这样的类型是不允许的。但是double &,double ,对象的引用或指针是正确的。

调用非类型模板形参的实参必须是一个常量表达式,即它必须能在编译时计算出结果。

调用非类型模板形参的实参必须是一个常量表达式,即它必须能在编译时计算出结果。

注意:任何局部对象,局部变量,局部对象的地址,局部变量的地址都不是一个常量表达式,都不能用作非类型模板形参的实参。全局指针类型,全局变量,全局对象也不是一个常量表达式,不能用作非类型模板形参的实参。

全局变量的地址或引用,全局对象的地址或引用const类型变量是常量表达式,可以用作非类型模板形参的实参。

sizeof表达式的结果是一个常量表达式,也能用作非类型模板形参的实参。

当模板的形参是整型时,调用该模板时的实参必须是整型的,且在编译期间是常量,比如

template <class T, int a> 
class A{};

int b;
A<int, b> m;  // error,b不是常量

// 改为下面形式
const int b;
A<int , b>m; // 正确,因为这时b是常量。

非类型形参一般不应用于函数模板中,比如有函数模板

template<class T, int a> 
void fun(T b){};

fun(2); // error,无法进行实参推演

对这种模板函数可以用显式模板实参来解决,如用下述方式就可以把非类型形参a设置为整数3

fun<int, 3>(2)

非类型模板形参和实参间所允许的转换
① 允许从数组到指针,从函数到指针的转换。

template <int * a> 
class A{}; 
int b[1]; 
A<b> m; // 即数组到指针的转换

② const修饰符的转换.。

template<const int *a> 
class A{}; 
int b; 
A<&b> m; // 即从int* 到const int *的转换。

③ 提升转换。

template<int a> 
class A{}; 
const short b = 2; 
A<b> m; // 即类似从short到int的类型提升转换

④ 整值转换。

template<unsigned int a> 
class A{};   
A<3> m; //即从int 到unsigned int的转换

6.模板的特例化

为什么需要特例化,因为在对不同类型的参数进行一些相同的处理时,如增容,拷贝等,不同类型的处理方式可能有些不同,如string增容时和其他类型就不同,它需要考虑深拷贝,所以要另外处理,这时候就用特例化。

例如,我们有以下函数模板:

template<typename T>
bool compare(const T &a, const T &b)
{
	return a > b; 
}
int main()
{
	int a = 10;
	int b = 20;
	compare<int>(a, b);
	return 0;
}

但该模板不适于字符串的比较,因此我们继续编写一个针对char*类型数据的特例化版本

template<>
bool compare<char *>( char* const &a,  char* const &b)
{
	return strcmp(a, b) > 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值