前言:在c++的学习中,模板是一个非常重要的知识点,虽然自己编程时很少去写的函数模板,类模板,但是在以后工作的时候,要经常去使用阅读别人写好的模板,而且要是以后要去看stl源码的话,理解c++经常使用的类是如何实现的,那对模板这一块的知识必须要有足够的了解。
函数模板
1.函数模板的语法
在一个函数之前写上
template <typename T>
void test1(){
}
但是我发现写成下面这样也没问题
template <class T>//参数T可以不止一个
void test1(){
}
2.函数模板编程练习——实现sort排序,swap函数,打印数组
#include <iostream>
#include <cstdlib>
using namespace std;
template<class T>
T mySwap(T &a ,T &b){
T temp = a;
a = b;
b = temp;
}
//利用选择排序,对int和char进行排序
template<class T>
void mySort(T *arr,int len){
for(int i = 0;i < len;i++){
for(int j = i+1;j < len;j++){
if(arr[i] > arr[j]){
mySwap<T>(arr[i],arr[j]);
}
}
}
}
template<class T>
void Print(T *arr,T len){
for(int i = 0;i<len;i++)
cout<<arr[i]<<endl;
}
void test01(){
char arr[] = {1,2,2,3,4,5,7,8,21};
char arr1[] = {'1','2','3','7','0','a','4','g','r'};
int len = sizeof(arr1)/sizeof(char);
mySort<char>(arr1,len);
Print<char>(arr1,len);
}
int main(){
test01();
return 0;
}
函数模板机制结论:
//编译器并不能把函数模板处理成能够处理的任何类型的函数
//函数模板通过集体类型产生不同的函数(通过函数模板产生模板函数)。
//所以编译器会对函数模板进行了两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码在进行一次编译。
模板的局限性: 如果代码实现是定义了赋值操作a=b,但是T为数组,或者是没有这种操作数的类型,这种假设就不成立了。
解决方案: 提供独特的具体化实现,如果可以在这种数据类型中重载此运算符,重载此运算符即可,当然,也可以特殊对待,写一个专门的函数来专门实现,样例入下。
#include <iostream>
using namespace std;
//模板不常写,但是常用,很多时候要去使用别人的模板
class Person
{
public:
string my_Name;
int m_Age;
Person(string name,int age)
{
this->my_Name = name;
this->m_Age = age;
}
//重载==运算符
bool operator==(Person &b) const
{
if(my_Name == b.my_Name && m_Age == b.m_Age){
return true;
}
else{
return false;
}
}
};
template <class T>
bool myCompare(T &a,T &b){
if(a == b){
return true;
}
else{
return false;
}
}
//专门为他重新写一个函数
template<>bool myCompare<Person>(Person &a,Person &b){
if(a.my_Name == b.my_Name && a.m_Age == b.m_Age){
return true;
}
else return false;
}
int main(){
return 0;
}
//普通函数能进行自动类型转化,函数模板不可以。
例如下面这个函数
template<class T>
T mySwap(T &a ,T &b){
T temp = a;
a = b;
b = temp;
}
如果传进去的是mySwap(int型,char型)
普通函数会进行自动类型转换,是是这个swap函数执行成功,而使用函数模板的话,编译器无法自动判断这个是哪一种类型,
因为两种类型不一样。
注:mySwap<int>(1,2) 和 mySwap(1,2)最后在函数模板中是一样的,编译器会自动判断1,2是int型数据,但是如果是类模板,就必须用第一种。
//普通函数可以和模板函数进行重载,优先调用普通函数,如果想要去强制走模板,要传一个空参数列表
//函数模板也可以发生函数重载
//如果函数模板可以产生更好的匹配,那么优先使用函数模板,比如如果要是普通函数要进行自动类型转换,函数模板不需要,那么使用函数模板
类模板
1.类模板的基础语法
//template下面紧跟的内容是类,那么这个类就是类模板
#include <iostream>
using namespace std;
template <class T>
class Base1
{
public:
Base1(){}
~Base1(){}
Base1(int){}
Base1(const Base1 &){}
Base1& operator=(const Base1&){}
T& operator[](int){}
private:
T* m_elements;
int m_size;
};
int main(){
return 0;
}
类模板编程练习
#include <iostream>
using namespace std;
//泛型编程思想 将类型参数化
//类模板成员函数的创建时机,创建出来两个类
//
template<class NAMETYPE,class AGETYPE = int//可以有默认值,定义的时候可以不用写
class Person
{
public:
NAMETYPE name;
AGETYPE age;
Person(NAMETYPE name,AGETYPE age){
this->age = age;
this->name = name;
}
};
int main(){
return 0;
}
2.类模板与函数模板的异同
不同处
//类模板可以有默认值
//类模板只能使用显示类型推导,不能用隐式
//类模板的类型 也可以有默认参数
相同处
同样每次要编译两次,下面这个代码可以证明
#include <iostream>
using namespace std;
class Person1
{
public:
void showPerson1()
{
cout<< "Person 1 show"<<endl;
}
};
class Person2
{
public:
void showPerson2()
{
cout<< "Person 2 show"<<endl;
}
};
template<class T>
class myClass
{
public:
T obj;
void fuc1()
{
obj.showPerson1();
}
//语法通过,他还不清楚传进去的参数是否可以有这些函数
//因为要编译两次
void fuc2()
{
obj.showPerson2();
}
};
void test01(){
myClass<Person1>a;
a.fuc1();
/* a.fuc2();//这里其实是不能实现的,因为里面a根本没这个函数 */
}
int main(){
test01();
return 0;
}
3.类模板当做参数—三种实现方法,注释里面也有解释
#include <bits/stdc++.h>
using namespace std;
//类模板当做参数的三种传入方法,常用第一种,比较简单,另外两个过于复杂,需要配合函数模板
template<class NAMETYPE,class AGETYPE = int>
class Person{
public:
NAMETYPE nametype;
AGETYPE age;
Person(NAMETYPE nametype,AGETYPE age){
this->age = age;
this->nametype = nametype;
}
};
//指定穿进去的参数
void doWork(Person<string,int> &a)
{
cout<<"daSDASDASDASDASDASD"<<endl;
}
//2参数模板化
template<class T1,class T2>
void doWork1(Person<T1,T2> &a){
cout<<typeid(T1).name()<<endl;
cout<<typeid(T2).name()<<endl;
cout<<"dasdasdasdasdawsdasd"<<endl;
}
//3整个类模板化
template<class T>
void doWork2(T &a){
cout<<typeid(T).name()<<endl;
cout<<"sdasdawsdasd"<<endl;
}
void test01()
{
Person<string,int>p1("aaa",10);
doWork(p1);
doWork1<string,int>(p1);
doWork2<Person<string,int>>(p1);
}
int main(){
test01();
return 0;
}