大家好啊,这里是c++之旅第九弹,跟随我的步伐来开始这一篇的学习吧!
如果有知识性错误,欢迎各位指正!!一起加油!!
创作不易,希望大家多多支持哦!
一.模版的概念:
1.泛型编程:
编写逻辑代码,适用于多种数据类型。
2.模版:
通过将类型定义为参数,实现了代码的重用性,即实现泛型编程。
二.函数模版:
1.函数模版不是实体函数,编译器不会对一个函数模板生成可执行代码;这只是一个函数功能的框架描述,只有在调用时有了具体的类型,生成了模版函数时才会生成可执行代码。
template:模板的关键字
<> :类型的参数列表
typename:用来声明类型参数,也可以用 class
2.函数模版使用:
int a=1,b=2;char c='x';template<typename T>//单类型void fun(T &a,T &b)//&不是必须的格式{cout<<a<<endl<<b<<endl;}template <typename T1,typename T2>//双类型定义方式void fun(T1 &a, T2 &b)
(1)、隐式推导类型:调用fun(a,b);正确 调用fun(a,c);错误,因为函数模板不会有隐式转换
(2)、显示指定类型:调用fun<int>(a,b);正确 调用fun<int>(a,c);也是正确的,因为显式指定类型后就生成了对应类型的实体函数,和普通函数一样,普通函数在编译器下可以进行可转换参数的转换,故此时也是正确的。//双类型的显式指定类型:fun<char, int>('a', 97);
(3)、函数模版类型参数列表中写普通形参就只能显式推导写法调用,eg:fun<int, 10>(12);不过一般这种写法不用于函数模版,因为可以将普通形参直接写在函数参数列表中,一般使用于类模板中。
3.函数模版和普通函数的区别:
(1)、函数模板和普通函数一样都是可以重载的,函数模板与普通函数也能构成重载
(2)、如果出现了函数模板与普通函数之间的重载,两者均满足时优先调用普通函数
(3)、如果函数模板可以产生一个更好的匹配,那么选择调用模板函数
(4) 、想要强制使用模板,那么就可以使用显式指定类型调用
4.函数模版的局限性:
eg:
template<typename T>void fun(T &a,T &b){cout<<a<<endl<<b<<endl;}
若想使用该模版函数,但是传入的类型为自定义类类型,这样这个函数就无法执行了。
解决方式:
重载一个具体类型的函数来解决这个问题,Person 为自定义类型,其中有一个可以访问到的 x 成员
template<>void fun<Person>(Person&a,Person&b){cout<<a.x<<endl<<b.x<<endl;}
三.类模版:
1.类模板与函数模板类似,也不是一个实体的类,理解为一个类型的框架,所以类模板也需要生成具体的模板类,才能定义对象。
(1)写法1(只有类型参数):
template<typename T1 ,typename T2>class 类模板名{成员函数和变量 ;}
T1,T2 看作为类型的参数,在类中使用
(2)写法2(在类型参数列表中有形参):(eg:栈的实现)
template <class T,int maxSize>class CMyStack //自定义顺序栈{ T buff[maxSize]; int top;public: CMyStack() { top = 0; } void push(T const& elem) { buff[top++] = elem; } void pop() { top--; } T getTop() const { return buff[top - 1]; } bool empty() const { return top == 0; }};CMyStack<int, 10> ms;//生成模板类后定义对象
2.类模板作为函数参数
(1)、做为函数的传入实参
void doWork(Person<string,int>& p){}
(2)、参数模板化
template<class T1,class T2>void doWork(Person<T1,T2> &p){}
(3)、整体模板化
template<class T>void doWork(T& p){}
3.类模板一般用于写数据结构类型,如数组,栈等。
4.类模板注意事项:
(1)类模板中成员函数要写在和类同一个头文件中类外的部分,不写到.cpp文件中
原因:因为.cpp文件是会参与编译的,而类模板在写的时候没有已知类型,故写在.cpp文件中的类中函数定义无法参与编译,也就相当于没有函数定义了;就算定义了一个模板类确定了类型,调用时写在.cpp文件中的函数体也不作数,使用时依然会报错(尝试得出),所以只有两种写法:直接定义在类中或者写在和类同一个头文件中的类外部分。
(2)template的类型参数T可以直接表示指针类型,但这样使用时会增加使用数据的不便,因为每次使用数据时都必须解引用进行操作,相对于普通类型(如int)的直接使用方式更加繁琐,所以一般也不会这样进行类型参数的使用。
(3)类模板继承情况:
①普通类的派生类为类模板情况:
class CA{};template <class T>class CB : public CA{};
②类模板的派生类新定义普通数据,但该派生类仍为类模板:
template <class T>class CD{ T d;};template <class T>class CE : public CD<T>{ int e;};
③两者均包含的是模版:
template <class T1,class T2>class CF : public CD<T1>{ T2 f;};
(4)函数模版有显式和隐式推导,类模版只有显式推导;
模版函数和模版类在生成相对应实体或进行使用之前是不会报出函数模版和类模版的书写错误的(即实体化后才会保错),因为不参与编译。
5.类模板使用场景:
(1)泛型编程,是一种编写通用代码逻辑的方法,使其能够适用于多种数据类型。通过使用模板,我们可以定义通用的数据结构(如数组、链表、栈、队列等)和算法(如排序、查找、遍历等),从而处理不同类型的数据。
(2)C++标准库中的容器类库(如vector、list、map等)也是使用模板实现的。这些容器类模板允许存储和操作各种类型的数据,提供了方便、高效且类型安全的数据结构。 (3)另外,C++标准库中的迭代器也是通过模板实现的。迭代器模板允许对容器中的数据进行遍历和访问,而无需关心具体的容器类型。使用迭代器模板可以提供一种通用的访问方式,使得不同类型的容器可以以统一的方式进行操作。