为什么需要模板?
面向对象的继承和多态机制,有效提高了程序的可重用性和可扩充性。而在程序的可重用性方面,要想得到更多的支持,就需要模板。
例如,当需要交换两个变量的值时:
void Swap(int &x,int &y)
{
int tmp=x;
x=y;
y=tmp;
}
以上的程序仅仅解决了int型变量的值,而当需要交换两个double变量的值时,还需要另外再编写一个函数。而这些Swap函数除了处理数据类型不同外,形式上都是一样的,能否只写一遍Swap函数,就能用来交换各种类型的变量值呢?
而有了模板,编译器就能在需要的时候,根据模板自动生成程序的代码。
什么是函数模板?
有了模板,可以只写一个Swap模板,编译器会根据Swap模板自动生成多个Swap函数,用以交换不同类型的值。函数模板的基本语法如下:
template <typename/class 类型参数>
template 告诉编译器,接下来是一个模板 ,typename 和 class 都是关键字,在这里二者可以互用没有区别。在< >中 T 叫做模板形参,一旦模板被实例化,T 也会变成具体的类型。
template <typename T>
T add(const T lva ,const T rva)
{
T a ;
a = lva + rva ;
return a;
}
这是一个模板函数的简单实例,所有模板函数在开始都需要 template 语句,以告诉编译器这是一个模板和参数等必要信息,当然里面的 T 可以取任意你喜欢的名字 ,模板参数个数也是任意更换的。 还要提醒的一点是:template <typename T1 ,typename T2 = int>函数模板是支持默认参数的,T1 、T2顺序在默认情况下是可以任意的,不用严格按照从右到左的顺序。
然后就是使用了,我们可以写出add(1,2) 这样的函数,也可以写出add(2.5,4.6)这样的函数,向 add 函数提供参数时,编译器会自动分析参数的类型,然后将所有用到 T 定义的换成相对性的类型,以上的两个函数在编译期间会生成。
int add(const int lva ,const int rva)
{
int a ;
a = lva + rva ;
return a;
}
double add(const double lva ,const double rva)
{
double a ;
a = lva + rva ;
return a;
}
需要注意的是,函数模板中可以有不止一个类型参数,与函数类似,只要函数模板的形参表不同,函数模板就可以重载。
什么是类模板?
编译器从类模板可以自动生成多个类,避免了程序员的重复劳动。
类模板的基本语法如下:
template<类型参数表>
class 类模板名
{
成员函数和成员变量
};
类型参数表的基本语法如下:
class 类型参数
template <class T>
class Myclass
{
T a;
public:
T add(const T lva ,const T rva);
};
template <class T>
T Myclass<T>::add(const T lva, const T rva)
{
a = lva + rva;
return a;
}
类模板定义对象的基本语法如下:
类模板名<真实类型参数表> 对象名(构造函数实参表)
Myclass<int> A; //用 int 实例化一个类A
Myclass<double> B; //用 double 实例化一个类B
当程序编译到这里时就会按照我们给出的类型,声明两组类和两组类函数。注意,在这里我们一定要显式给出类型 T 。类模板不像是函数模板 ,函数模板会根据参数推断类型。 当然类模板也支持默认参数,但是类模板必须严格从右往左默认化。
类模板中成员模板的基本语法如下:
template<类型参数表>
返回值类型 类模板名<类型参数表>::成员函数名(参数表)
{
...
}
template <class T>
class Myclass
{
public:
T a;
template <typename type_1 , typename type_2>
type_1 add(const type_1 lva ,const type_2 rva);
};
template <class T>
template <typename type_1,typename type_2>
type_1 Myclass<T>::add(const type_1 lva, const type_2 rva)
{
a = lva + rva;
return a;
}
在类的声明中使用了一个嵌套的模板声明。且通过作用域运算符 :: 指出 add 是类的成员。
模板类中的静态成员
在类中定义的静态成员是存储在静态区中,被所有类对象共享,并不属于某一个类所有,同样的在模板类中的静态成员也不会被复制多份,而是被同类实例化的类对象共享。
typename和class的区别是什么?
typename和class是模板中经常使用的两个关键词 ,在模板定义的时候没有什么区别。以前用的是 class,后来 c++ 委员会加入了 typename。因为历史原因,两个是可以通用的。对有些程序员来说,在定义类模板的时候,常常使用 class 作为关键字,增加代码可读性。其它则用 typename,上面的代码大都遵循这样的标准,但是并无强制规定。但是如果二者没有差别,为什么还要加入typename呢?
class Myclass{
public:
Myclass();
typedef int test; //定义类型别名
}
template <class T>
class Myclass2{
public:
Myclass2();
T::test *a // 声明一个指向T::test类型的指针。
// typename T::test * a
}
在 c++ 中,允许我们在类中定义一个类型别名,且使用的时候和类名访问类成员的方法一样。这样编译器在编译的时候就会产生二义性,它根本不知道这是一个类型还是别名,所以我们加上 typename 显式说明出来。当然如果这里没有二义性,比如Myclass ::test * a ,加上 typename 是会报错的。此外,在 class 的 STL 底层还有一个特性,用于保留模板参数,但是在 c++17 中已经舍弃。
本节内容整理自实验楼
来源:CSDN
作者:咸鱼_
链接:https://blog.csdn.net/qq_35177575/article/details/104214284