模板
学习模板,使用模板让我们的代码能最大限度上的重用
一、模板的概念
1、模板与泛型编程
1.泛型编程:指的就是编写与类型无关的逻辑代码,在泛型编程中,编写的代码可以作用多种类型的对象
2.模板:就是实现代码重用的一种工具,它可以实现类型参数化,即把类型定义为参数,从而实现了代码的重用性。模板就是泛型编程的基础
2、模板分类
1.函数模板
2类模板
二、函数模板
1、函数模板是什么
1.函数模板不是一个实体函数,编译器不会对一个函数模板生成可执行代码。定义函数模板之后只是说对一个函数功能框架的一个描述,只有在调用的时候才会为其生成可执行代码
2、函数模板的定义
1.定义:
template<typename T>
返回值类型 函数名(参数列表)
{
函数体;
}
template:模板的关键字
<>:类型的参数列表
typename:用来声明类型参数,也可以用class
T:形参类型的名字
//注意:类型的参数列表中是可以有多个类型的,比如tempplate<typrname T1,typename T2>,写法和函数的形参列表写法一样
3、函数模板示例
template<typename T>
void fun(T &a,T &b)
{
cout<<a<<endl<<b<<endl;
}
//定义:
int a=1,b=2;
char c='x';
4、函数模板和普通函数
1.函数模板和普通函数一样都是可以重载的,函数模板与普通函数也能构成重载
template<typename T>
void fun(T a, T b)
{
cout << "我是函数模板" << endl;
}
void fun(int a, int b)
{
cout << "我是普通函数" << endl;
}
template<typename T1, typename T2>
void fun(T1 a, T2 b)
{
cout << "我是第二个函数模板" << endl;
}
//fun(1,'A');
//这样是调用的第三个fun函数,因为他会优先调用更好匹配的函数模板
2.如果出现了函数模板与普通函数之间的重载,优先调用普通函数
3.如果函数模板可以产生一个更好的匹配,那么选择模板
4.想要强制使用模板,那么就可以使用显示指定类型调用
5.函数模板实际上就是多了一个类型的参数列表,让我们在使用这个函数模板的时候可以传类型,定义了一个函数的框架,而不用确定类型,可以使用这个函数模板的时候再来确定类型
#include<iostream>
using namespace std;
//模板头,定义了类型的参数列表,可以传一个类型进来
//这个类型T可以在这个模板头下面的函数中进行使用
template<typename T>
T fun(T a, T b)
{
return a + b;
}
/*
在调用的时候,编译器会把函数模板转为模板函数(确定T的类型)
int fun(int a,int b)
{
return a+b;
}
*/
//隐式调用:函数模板会帮助我们自动去推导这个T的类型是什么类型
//函数模板的类型只有一个任意类型
//显示指定类型:给函数模板的类型参数,传一个具体的参数(类型),是可以有类型的自动转换的
int main()
{
cout << fun(1, 2) << endl;//隐式调用
cout << fun(1.2, 2.4) << endl;
int x = fun('A', 'A');
cout << x << endl;//-126----》中间要经过0
cout << fun<char>(1, 'A') << endl;//显示指定类型,输出B
fun<>(1,2)//空参数列表也能强制调用函数模板
//cout<<fun(1,'a');这样是不行的,必须显示指定类型
//cout<<fun<int>(1,'a');空参数列表也不行
system("pause");
return 0;
}
5、模板的局限性
//模板在具体使用的过程中还是会出现一些问题
template<class T>
bool mycompare(T&a,T&b)
{
if(a==b)
{
return true;
}
return false;
}
//这个函数模板在对于基本数据类型的比较是没有太大问题的,但是如果T是一个自定义数据类型,那么就不一定能用==来进行比较,所以在这里我们可以在自定义类型中加上重载运算符==来解决这个问题或者通过一个重载函数来处理
class person
{
public:
int x;
}
//重载函数
template<>bool mycompare<person>(perosn&a,person&b)
{
if(a.x==a.x)
{
return true;
}
return false;
}
//解决方法
#include<iostream>
using namespace std;
template<class T>
bool mycompare(T&a, T&b)
{
if (a == b)
{
return true;
}
return false;
}
class person
{
public:
int x;
};
struct node
{
int a, b;
node()
{
a = 1, b = 2;
}
};
//在c++中的结构体里面可以写的东西:在类中可以写的,在结构体里面也可以写,比如构造析构
//因为不能比较两个类
//解决方法:1.运算符重载==
//2.通过语法重载解决这个没有compare函数
//template<>返回值类型 函数名<具体的类型>(具体的类型定义的参数列表)
//3.单独写个函数比较,不用模板
template<>bool mycompare<person>(person&a, person&b)
{
if (a.x == b.x)
return true;
return false;
}
//重载==
bool operator==(person& a, person& b)
{
if (a.x == b.x)
return true;
return false;
}
int main()
{
person p1, p2;
p1.x = 10;
p2.x = 10;
cout << mycompare(p1, p2) << endl;
system("pause");
return 0;
}
三、类模板
1、类模板是什么
1.类模板与函数模板类似,也不是一个实体的类,理解为一个类的框架
2、类模板定义
template<类型参数列表>
class 类模板名
{
成员函数和变量
}
3、类模板示例
template<class nametype,class agetype=int>//类模板也可以有默认类型,这是函数缺省
class person
{
public:
nametype name;
agetype age;
person(nametype name,agetype age)//在类模板中直接定义成员函数
{
this->name=name;
this->age=age;
}
agetype getage();//在类模板中声明成员函数,在类外定义
}
template<class nametype,class agetype>//类模板外定义刚声明的函数
agetype person<nametype,agetype>::getage()
{
return age;
}
4、类模板--动态数组
#include<iostream>
using namespace std;
template<typename T/*,typename T1,typename T2=int*/>
//类模板的类型参数同样可以缺省
class myarr
{
T buff[100];//定义了一个数组
int len;
public:
myarr()//构造函数,初始化
{
memset(buff,0, sizeof(T) * 100);//初始化数组
len = 0;
}
void insert(int data)
{
buff[len++] = data;
}
void print()
{
for (int i = 0; i < len; i++)
{
cout << buff[i] << " ";
}
cout << endl;
}
};
int main()
{
myarr<int> m1;//类模板不支持隐式推导类型,必须使用显示指定类型调用
for (int i = 0; i < 10; i++ )
{
m1.insert(i + 1);
}
m1.print();
myarr<char> m2;
for (char i = 'a'; i <= 'z'; i++)
{
m2.insert(i);
}
m2.print();
system("pause");
return 0;
}
5、类模板做函数的参数
1、作为函数的传入实参
void dowork(perosn<string,int>&p){}
//把一个具体的类作为函数的参数
2、参数模板化
template<class T1,class T2>
void dowork(perosn<T1,T2>&p){}
//函数也是模板,且模板中的参数继续给类模板作为参数
3、整体模板化
template<class T>
void dowork(T&p){}
//可以把perosn<string,int> 整体当成T类型,比如
//perosn<string,int> myperson;
//dowork(myperson);
#include<iostream>
#include<string>
using namespace std;
template<class T1,class T2>//定义两个类模板
class person
{
public:
T1 name;
T2 age;//定义两个类模板里的成员
person(T1 name, T2 age)
{
this->age = age;
this->name = name;
}
void showperson()
{
cout << name << " " << age << endl;
}
};
//拿具体的类类型当做形参,来接收实参
void fun(person<string, int>&p)
{
p.showperson();
}
//参数模板化
template<class T1,class T2>
void fun1(person<T1, T2>&p)
{
p.showperson();
}
//整体模板化
template<class T>
void fun2(T&p)
{
p.showperson();
}
int main()
{
person<string, int> p1("张飞", 500);
p1.showperson();
fun(p1);
fun1(p1);//可以不写参数
fun2(p1);
system("pause");
return 0;
}
6、类模板和继承
#include<iostream>
#include<string>
using namespace std;
template<class T1>
class base
{
public:
T1 a;
};
template<class T1,class T2>
class child :public base<T1>
{
public:
T2 b;
};
//如果父类是类模板,那么在定义子类的对象的时候,是需要构造父类的成员的
//如果父类的这个任意类型T1不知道是什么类型,那么无法定义成员a(无法定义父类的成员),就需要在继承的时候,告诉父类的这个T1是什么类型
int main()
{
child<int, int> c;
system("pause");
return 0;
}
7、类模板不能分模板编写
1.类模板中成员函数的创建时在调用阶段,那么就会导致在分文件编写的时候链接不到,即不能分文件编写类模板
2.解决方法,直接将声明和实现写到同一个文件中,把后缀名改为.hpp,.hpp是一个约定俗成的名称
person.h
写在同一个文件下面
#pragma once
#include<stdio.h>
template<class T>
class person
{
public:
T n;
void fun();
};
template<class T>
inline void person<T>::fun()
{
printf("aaaa");
}
源.cpp
#include<iostream>
#include"person.h"
using namespace std;
int main()
{
person<int> p;
p.fun();
system("pause");
return 0;
}
#include<iostream>
using namespace std;
class A
{
public:
void Afun()
{
cout << "A的fun函数" << endl;
}
};
class B
{
public:
void Bfun()
{
cout << "B的fun函数" << endl;
}
};
template<class T>
class CC
{
T t;
public:
void funa()
{
t.Afun();
}
void funb()
{
t.Bfun();
}
T fun1(T a);
void fun2();
};
template<class T>
T CC<T>::fun1(T a)
{
return T;
}
template<class T>
void CC<T>::fun2()
{
}
int main()
{
CC<A> c;
c.funa();
system("pause");
return 0;
}
类模板中实现友元函数
#include<iostream>
using namespace std;
//类模板中实现友元函数
template<class T>
class person
{
T id;
public:
friend void print(person<T>&p)
cout << p.id << endl;
person(T id)//带参构造
{
this->id = id;
}
};
int main()
{
person<int> p(99);
print(p);
system("pause");
return 0;
}
类外实现友元函数---较麻烦
#include<iostream>
using namespace std;
//类模板向前声明
template<class T>
class person;
//函数模板向前声明
template<class T>
void print(person<T>&p);
template<class T>
class person
{
T id;
public:
person(T id)//带参构造
{
this->id = id;
}
friend void print<>(person<T>&p);
};
template<class T>
void print(person<T>&p)
{
cout << p.id << endl;
}//这里只是一个逻辑代码
int main()
{
person<int> p(33);
print(p);
system("pause");
return 0;
}