07 模板编程
模板
C++的泛型编程利用的主要就是模板
模板主要分为函数模板 和 类模板
一、函数模板
函数模板的作用:
建立一个通用的函数,其返回值和形参类型可以不具体确定,用一个虚拟的类型来代表。
1.语法:
1 | template<typename T>//声明一个模板,告诉编译器后边的T是一个通用数据类型 |
template:关键字,声明创建模板
typename:关键字,可以用class替代,表面其后面的符号是一种数据类型
T:通用的数据类型,也可换成其他的符号
2.注意:
如果把函数的声明和实现分别放到.h和.cpp中,则要写两次template
template< >仅对紧跟的一个函数的声明或者定义有效,如果要写多个函数模板,则要写多次template< >
3.使用方法
1 | //1.自动推导数据类型 |
模板必须确定出T的数据类型,函数才能正常运行
4.函数模板和普通函数的区别
普通函数可以发生自动类型转换(隐式类型转换)
自动类型推导的函数模板不能发生隐式类型转换
显示指定数据类型的函数模板,可以发生隐式类型转换
5.函数模板和普通函数重载时的调用规则
如果函数模板和普通函数都能实现,优先调用函数模板
可以通过空模板参数列表< >来强制调用函数模板
函数模板本身也可以重载
如果函数模板可以产生更好的匹配,则优先调用函数模板
6.模板可以针对特定的数据类型重载
语法:
template< > 返回值类型 函数名(特定数据类型 a , 特定数据类型 b….)
1 | template<typename T> |
如果传入数据类型T为一个用户自定义的类,则该模板函数就不能用了,用户需要针对该类重载一个模板函数
1 | template<> void func(Person &a, Person &b) |
二、类模板
类模板的作用:
创建一个通用类,类中成员的数据类型可以不具体指定,而用一个虚拟的类型代替。
1.语法:
1 | template<class T1,class T2 ....> |
注意:
用类模板实例对象时,必须要在模板参数列表< >中指定数据类型
类不能重载,因此类模板不能和已有的类重名
2.类模板和函数模板的区别
类模板不能自动推导数据类型,只能显式指定
类模板可以在模板参数列表中有默认的参数,如:
1 | template<typename Type_name, typename Type_height = int> |
3.类模板中成员函数的创建时机
普通类的成员函数在类被定义好后就会被创建
模板类的成员函数只有在具体的一个实例调用该函数时才会被创建
4.类模板作为函数的参数
类模板有三种方法可以作为函数的参数:
1.传入指定的数据类型—直接在形参列表显式指定数据类型
1 | void test01(Pen<string, int> &p) |
2.参数模板化—将对象中的参数变成模板进行传递
1 | template <class T1, class T2> |
3.整个类模板化—将这个对象类型 模板化进行传递
1 | template <class T1> |
1 | int main() |
5.类模板和继承
当子类继承一个类模板的时候,如果想让这个子类不是类模板而是一个确定的类,则需要在继承时在模板参数列表中写清楚数据类型
1
2
3
4
5
6
7
8template<class T1,class T2>
class Base
{
T1 m_age;
T2 m_weight;
};
class Son : public Base<int,float>如果想让子类的数据类型也是灵活可变的,则子类也要写成类模板的形式
1
2
3
4
5template<class T3,class T4>
class Son1 : public Base<T3,T4>
{
};
6.类模板成员函数的类外实现
如果要把类外成员函数的实现和声明分开写的话:
每个成员函数上一行都得写一次template< >
作用域也要加上模板参数列表
如:
1 | template<class T1,class T2> |
1 | template<class T1, class T2> |
7.类模板的分文件编写
由于类模板成员函数只有在被实例调用的时候才会被创建,因此像写普通类那样将声明和实现分别放到.h 和 .cpp文件就会在编译时出现链接错误。
解决方法:
直接 #include “xxx.cpp”,不推荐这样
将类模板的声明和实现写到一个文件内,并把后缀改成.hpp(不改写成.h也可以,不过约定俗成是.hpp),然后再#inlcude “xxx.hpp”就行了