模板
模板 泛型编程 类型参数化 C++高级语法
将类型参数化,实现算法与类型的分离,编写针对类型更加抽象的函数和类
sort() 可以对任意类型的数据进行排序 泛型
vector 可以存储任意类型的数据
函数模板
实现通用的函数,将函数的类型参数化
语法格式
template<typename T> //template<类型形式参数表> 泛型参数
void func(T t){}
template<类型形式参数表> 类型参数列表可以有多个类型
template<typename T1,typename T2,...>
template 关键字
typename 关键字 ------ class
函数模板中的类型参数一般用于函数的形参列表,但也可以用于函数体中定义变量
函数模板中的类型参数一般作用于形参,那么如果在调用该函数时,
可以根据实参的类型进行类型推导,从而不需要指定模板类型
注意,有的时候根据实参也不能推导出来,必须显示提供类型参数
如果函数模板类型作用于函数体中,在调用函数时只能显示传递类型进行实例化
所谓的函数模板其实好比一个类型,需要用具体的类型来实例化成函数(模板函数)
函数模板 用具体的类型实例化 模板函数
#include <iostream>
#include <algorithm>
using namespace std;
template<typename T>
void mswap(T& t1,T& t2){
T t = t1;
t1 = t2;
t2 = t;
}
template<typename T>
void msort(T elems[],size_t cnt){
for(int i=0;i<cnt;i++){
bool isswap = false;
for(int j=1;j<cnt-i;j++){
if(elems[j] < elems[j-1]){//改变这个 就能改变排序规则
mswap(elems[j],elems[j-1]);
isswap = true;
}
}
if(!isswap){
break;
}
}
}
template<typename T>
void print(T elems[],size_t cnt){
for(int i=0;i<cnt;i++)
cout << elems[i] << " ";
cout << endl;
}
class Stu{
private:
int no;
string name;
int s;
public:
Stu(int no,string name,int s):no(no),name(name),s(s){}
int getNo()const{
return no;
}
int getS()const{
return s;
}
friend ostream& operator<<(ostream& os,const Stu& s);
};
ostream& operator<<(ostream& os,const Stu& s){
return os << s.no << " " << s.name << " " << s.s << endl;
}
//对象 函数对象
class Comp{
public:
bool operator()(const Stu& s1,const Stu& s2){
return s2.getS() < s1.getS();
}
};
int main(){
int arr[9] = {7,2,9,8,1,4,5,0,6};
msort(arr,9);
print(arr,9);
Stu srr[5] = {Stu(110,"jack",90),Stu(120,"lily",97),
Stu(119,"lucy",89),Stu(127,"james",99),Stu(133,"jordan",97)};
sort(srr,srr+5,Comp());//匿名对象 匿名函数对象
print(srr,5);
return 0;
}
C++为什么能够支持模板这种语法?
C++支持重载
函数模板根据类型实例化之后,生成了函数名相同,参数列表不同的重载函数
调用函数模板
函数模板要先根据类型实例化 产生具体的 模板函数
实际上调用的是 模板函数
函数模板: 是模板 类 需要实例化对象 这个对象 称为 模板函数
模板函数: 是函数 可以调用
函数名(实参列表); //自动类型推导
函数名<类型参数>(实参列表); //不能进行类型推导
#include <iostream>
using namespace std;
template<typename T>
T msum(T a,T b){
return a+b;
}
int main(){
cout << msum(1,2) << endl;
//cout << msum(1,3.1) << endl; //不能根据实参进行类型推导
cout << msum<double>(1,3.1) << endl;
cout << msum(1.0,3.1) << endl;
return 0;
}
特化版本的定义
函数模板有可以针对某些特殊的类型不太适用,可以为该类型编写一个特化版本的函数模板
template<>
RET_TYPE func_template_name<特化类型>(arglist...){}
#include <iostream>
#include <cstring>
using namespace std;
template<typename T>
T mmax(T a,T b){
return a>b?a:b;
}
//针对特殊的类型 特化的函数模板
template<>
const char* mmax<const char*>(const char* a,const char *b){
return strcmp(a,b)>0?a:b;
}
int main(){
cout << mmax("ABC","abc") << endl;//调用特化版本的函数模板
cout << mmax(3.14,5.16) << endl;
return 0;
}
函数模板有点类似于C语言中宏函数
#define MAX(a,b) ((a)>(b)?(a):(b))
int a = 1,b = 0;
MAX(++a,b); // (++a)>(b)?(++a):(b)
但是宏容易产生意想不到的错误
类模板
类模板定义语法
template<typename T,...>
class class_template_name{};
类模板不能根据参数进行类型推导实例化模板类,只能显示
class_template_name<类型参数> 对象名(实参列表);// 这一行代码有两个实例化
1.根据类模板加上类型参数 实例化成 模板类(具体类)
2.根据生成的模板类 实例化 一个对象
栈的模板
#include <iostream>
#include <stdexcept>
using namespace std;
//类模板
template<typename T=int>
class Stack{
private:
T *elems;
size_t size;
size_t cap;
public:
Stack(size_t cap = 10):cap(cap){
elems = new T[cap];
size = 0;
}
~Stack(){
if(elems != NULL)
delete [] elems;
elems = NULL;
}
Stack(const Stack& s):elems(new T[s.cap]),size(s.size),cap(s.cap){
for(int i=0;i<size;i++)
elems[i] = s.elems[i];
}
Stack& operator=(const Stack& s){
if(this != &s){
Stack stmp(s);
swap(stmp.elems,elems);//swap(stmp,*this); 调用 operator=
size = s.size;
cap = s.cap;
}
return *this;
}
bool full(){
return size == cap;
}
bool empty(){
return size == 0;
}
void push(const T& data){
if(full()){
throw overflow_error("栈上溢");
}
elems[size++] = data;
}
T pop(){
if(empty()){
throw underflow_error("栈下溢");
}
return elems[--size];
}
T top(){
if(empty()){
throw underflow_error("栈下溢");
}
return elems[size-1];
}
};
class Stu{};
int main(){
Stack<> stack;
//常引用
int a;
int& r = a;
const int& rc = a;
//int& r1 = 1;
const int& r2 = 1;//常引用
Stack<int> s(6);
Stack<double> s1;
Stack<Stu> s2(5);
s.push(1);
s.push(2);
s.push(3);
s.push(4);
s.push(5);
s.push(6);
try{
s.push(4);
}catch(overflow_error& e){
cout << e.what() << endl;
}
while(!s.empty()){
cout << s.pop() << endl;
}
return 0;
}
//队列模板(自己尝试写一下)
template<typename T>
class Queue{};
类模板的类型缺省
template<typename T=int>
class Comptor{}
Comptor<> c2(3,4);
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
template<typename T=int>
class Comptor{
private:
T x;
T y;
public:
Comptor(const T& x,const T& y):x(x),y(y){}
T max(){
return x>y?x:y;
}
T min(){
return x<y?x:y;
}
};
//全特化
/*
template<>
class Comptor<const char*>{
private:
const char *x;
const char *y;
public:
Comptor(const char* & x,const char* & y):x(x),y(y){}
const char* max(){
return strcmp(x,y)>0?x:y;
}
const char* min(){
return strcmp(x,y)<0?x:y;
}
};
*/
//成员物化
template<>
const char *Comptor<const char*>::max(){
return strcmp(x,y)>0?x:y;
}
template<>
const char *Comptor<const char*>::min(){
return strcmp(x,y)<0?x:y;
}
int main(){
Comptor<int> c1(1,2);
Comptor<> c2(3,4);
cout << c2.max() << endl;
cout << c2.min() << endl;
const char *s1 = "hello";
const char *s2 = "hi";
Comptor<const char*> c3(s1,s2);
cout << c3.max() << endl;//
cout << c3.min() << endl;
return 0;
}
通用的类模板
template<typename T,...>
class cla_template_name{
};
全特化
template<>
class cla_template_name<特化类型>{
};
template<>
class Comptor<const char*>{
private:
const char *x;
const char *y;
public:
Comptor(const char* & x,const char* & y):x(x),y(y){}
const char* max(){
return strcmp(x,y)>0?x:y;
}
const char* min(){
return strcmp(x,y)<0?x:y;
}
};
成员特化
template<>
ret_type cla_template_name<特化类型>::memb_func_name(arglist,..){}
template<>
const char *Comptor<const char*>::max(){
return strcmp(x,y)>0?x:y;
}
template<>
const char *Comptor<const char*>::min(){
return strcmp(x,y)<0?x:y;
}
类模板在实例化时一定要显示类型实例化
类模板可以有缺省类型,类模板也可以有普通模板参数(非类型模板参数)
类模板的非类型参数 及 模板缺省值
1.非类型参数的实参仅限于常量或者常量表达式
//int a = 10;
//Array<int,a> arr4;//这里a不是常量,所以这样是错的
const int ci = 10;
Array<int,ci> arr5;//c++中const定义的是常量,这样定义正确
2.无论是类型参数还是非类型参数,都可以带缺省值,但是和函数的缺省值一样
要满足靠右原则
#include <iostream>
using namespace std;
//类模板的缺省参数 靠右原则
template<typename T=int,int LEN=5>
class Array{
private:
T arr[LEN];
public:
const T& operator[](int index)const{
return arr[index];
}
T& operator[](int index){
return arr[index];
}
};
int main(){
//Array arr; <>括号必须得写
Array<> arr1;
arr1[0] = 1;
arr1[1] = 100;
Array<double> arr2;
Array<string,4> arr3;
arr3[0] = string("Hello");
int a = 10;
//Array<int,a> arr4;
const int ci = 10;
Array<int,ci> arr5;
return 0;
}
局部特化(类中的成员函数是模板) 偏特化
1.普通的类 某些成员函数是模板
#include <iostream>
#include <typeinfo>
using namespace std;
//普通的类 不是类模板
class A{
public:
//成员函数模板
template<typename T>
void func(T t){
cout << typeid(T).name() << endl;
}
template<typename T>
void func(){
T t;
cout << typeid(T).name() << endl;
}
void show(){
cout << typeid(*this).name() << endl;
}
};
int main(){
A a;
a.show();
a.func(1);
a.func(3.14);
a.func(a);
a.func(3.14);
a.func<float>(3.14);
a.func<int>();
a.func<A>();
a.func<double>();
return 0;
}
2.针对指针和数组进行偏特化
3.针对模板类型偏特化
#include <iostream>
using namespace std;
//针对类型局部特化
template<typename T,typename N>
class A{
public:
A(){
cout << "A<T,N>" << endl;
}
};
//偏特化
template<typename T>
class A<T,int>{
public:
A(){
cout << "A<T,int>" << endl;
}
};
template<>
class A<int,int>{
public:
A(){
cout << "A<int,int>" << endl;
}
};
template<typename T>
class B{
public:
B(){
cout << "B<T>" << endl;
}
};
//针对指针实现偏特化
template<typename T>
class B<T*>{
public:
B(){
cout << "B<T*>" << endl;
}
};
//针对数组实现偏特化
template<typename T>
class B<T[]>{
public:
B(){
cout << "B<T[]>" << endl;
}
};
template<typename T1,typename T2,typename T3>
class C{
public:
C(void){
cout << "C<T1,T2,T3>" << endl;
}
};
template<typename T1,typename T2>
class C<T1,T2,T2>{
public:
C(void){
cout << "C<T1,T2,T2>" << endl;
}
};
template<typename T>
class C<T,T,T>{
public:
C(void){
cout << "C<T,T,T>" << endl;
}
};
template<typename T>
class C<T,T*,T*>{
public:
C(void){
cout << "C<T,T*,T*>" << endl;
}
};
int main(){
//选择特化程序更高的版本
A<int,int> a; //全特化 A<int,int>
A<double,int> b; //偏特化 A<T,int>
A<short,double> c; //通用 A<T,N>
//针对指针和数组进行偏特化 如果类型为指针和数组会选择指针和数组偏特化的版本
B<int*> d;
B<int[]> e;
B<int> f;
//匹配度高的特化优先
C<int,int,int> g;
C<int,int*,int*> h;
return 0;
}
模板继承、模板成员问题:
1.从类模板继承 普通类 类模板
#include <iostream>
using namespace std;
//类模板
template<typename T>
class A{
private:
T t;
public:
A(const T& t):t(t){
cout << "A构造" << endl;
}
T& get(){
return t;
}
const T& get()const{
return t;
}
};
//普通类
class B:public A<int>{
private:
int m;
public:
B(int a,int b):A(a),m(b){
cout << "B构造" << endl;
}
void show()const{
cout << get() << ":" << m << endl;
}
};
//类模板继承类模板
template<typename T,typename N>
class C:public A<T>{
private:
N n;
public:
C(const T& t,const N& n):A<T>(t),n(n){//调用基类类模板的构造函数需要类型参数
cout << "C构造" << endl;
}
void show(){
cout << A<T>::get() << ":" << n << endl;//调用基类类模板的普通函数类型参数
}
};
int main(){
B b(1,200);
b.show();
cout << b.get() << endl;
C<int,int> c(3,4);
c.show();
return 0;
}
以前写的Shape Rect Circle实例
#include <iostream>
using namespace std;
template<typename BASE>
class Shape:public BASE{
public:
void draw(void)const{
BASE::draw();
};
};
class Rect{
public:
void draw(void)const{
cout << "draw rect..." << endl;
}
};
class Circle{
public:
void draw(void)const{
cout << "draw circle..." << endl;
}
};
int main(){
//Shape<int> s;//Shape:public int 错误的
Shape<Rect> s1;
Shape<Circle> s2;
s1.draw();
s2.draw();
return 0;
}
2.向类模板派生
#include <iostream>
using namespace std;
template<typename BASE>
class Shape:public BASE{
public:
void draw(void)const{
BASE::draw();
};
};
class Rect{
public:
void draw(void)const{
cout << "draw rect..." << endl;
}
};
class Circle{
public:
void draw(void)const{
cout << "draw circle..." << endl;
}
};
int main(){
//Shape<int> s;//Shape:public int 错误的
Shape<Rect> s1;
Shape<Circle> s2;
s1.draw();
s2.draw();
return 0;
}
3.类模板中的模板成员
a.模板型成员变量
#include <iostream>
using namespace std;
template<typename T>
class A{
private:
T t;
public:
A(const T& t):t(t){
cout << "A" << endl;
}
T& get(){
return t;
}
};
//类模板
template<typename T,typename N>
class B{
private:
A<T> a;//类模板作为类的成员变量
N b;
public:
B(const T& t,const N& b):a(t),b(b){
cout << "B" << endl;
}
void show(){
cout << a.get() << ":" << b << endl;
}
};
class C{
private:
A<string> a;
public:
C(const string& s):a(s){
cout << "C" << endl;
}
void show(){
cout << a.get() << endl;
}
};
int main(){
B<string,int> o("hello c++",1024);
o.show();
C c("hello java");
c.show();
return 0;
}
b.模板型成员函数
#include <iostream>
using namespace std;
template <typename T>
class A{
private:
T t;
public:
A(const T& t):t(t){
cout << "A" << endl;
}
/*
template<typename N>
void show(const N& n){
cout << t << ":" << n << endl;
}
template<typename N>
void bar(){
N n;
cout << "bar" << endl;
}
*/
template<typename N>
void show(const N& n);
template<typename N>
void bar();
};
template<typename T>
template<typename N>
void A<T>::show(const N& n){
cout << t << ":" << n << endl;
}
template<typename T>
template<typename N>
void A<T>::bar(){
N n;
cout << "bar" << endl;
}
int main(){
A<string> a("Hello");
a.show<int>(100);
a.bar<int>();
return 0;
}
c.模板型成员类型
#include <iostream>
using namespace std;
template<typename T,typename M>
class A{
public:
//模板型成员类型
/*
template<typename N>
class B{
public:
B(const N& n):n(n){
}
N n;
};
*/
template<typename N> class B;//声明
A(const T& t,const M& m):t(t),m(m){
}
void show(){
cout << t << ":" << m.n << endl;
}
private:
T t;
B<M> m;//M --> N
};
template<typename T,typename M>
template<typename N>
class A<T,M>::B{
public:
N n;
B(const N& n):n(n){
}
};
int main(){
A<string,int> a("hello",1024);
a.show();
return 0;
}
4.模板型模板实参
template<typename X,typename Y,template<typename T> class Z>
Z必须是一个类模板 而不是具体的类型
#include <iostream>
#include <vector>
using namespace std;
template<typename T>
class A{
private:
T t;
public:
A(const T& t):t(t){
}
T get(){
return t;
}
};
template<typename X,typename Z,template<typename T> class Y> //类模板的第三个类型参数一定是类模板
class B{
public:
Y<X> x;
Y<Z> z;
B(const X& x,const Z& z):x(x),z(z){
}
};
int main(){
B<string,int,A> b("Hello",1024);
cout << b.x.get() << endl; //A<string>
cout << b.z.get() << endl; //A<int>
//vector<vector<int>> v; C++11之前 >> 一定要有空格
vector<vector<int> > v;
return 0;
}
STL 用模板的语法实现一些通用容器和算法