模板
使用模板的程序设计,就是泛型程序设计
函数模板
void Swap(int& x,int& y)
{
int tmp=x;
x=y;
y=tmp;
}
void Swap(double& x,double& y)
{
double tmp=x;
x=y;
y=tmp;
}
//这两个函数时重载关系
//能否只写一个Swap就能交换各种类型的变量
形式:
template<class 类型参数1,class 类型参数2,……> 返回值类型 模板名(形参表) { 函数体 };
eg: template<class T> //T就是类型参数 void Swap(T& x,T &y) { T tmp=x; x=y; y=tmp; }
#include<iostream>
using namespace std;
template<class T>
void Swap(T &a,T &b)
{
T tmp=a;
a=b;
b=tmp;
}
int main()
{
int n=1,m=2;
cout<<n<<" "<<m<<endl;
Swap(n,m);
//编译器自动生成具体类型
//eg 生成void Swap(int &a,int &b);
cout<<n<<" "<<m<<endl;
double x=1.9,y=8.1;
cout<<x<<" "<<y<<endl;
Swap(x,y);
cout<<x<<" "<<y<<endl;
}
//!!!函数模板中可以有不止一个类型参数
template<class T1,class T2>
T2 Print(T1 arg1,T2 arg2)
{
……
}
//求数组最大元素的MaxElement函数模板
#include<iostream>
using namespace std;
template<class T>
T MaxElement(T* a,int size)
{
T tmpMax=a[0];
for(int i=1;i<size;i++)
{
if(tmpMax<a[i])
{
tmpMax=a[i]
}
}
return tmpMax;
}
//就可以满足各种数据类型的数组传进来
将模板数据类型补充,称为模板的实例化
编译器走到这一步就实例化完全
不通过参数实例化函数模板
#include<iostream>
using namespace std;
template<class T>
T Inc(T n)
{
return n+1;
}
int main()
{
cout<<Inc<double>(4)/2<<endl;//=>2.5
//即使你传进去的是int 4,但是输出依然是浮点数
//首先尊重double的声明
//不通过参数实例化,就是我已经很确定这一句运行时,应该是什么数据类型
//<表明类型>(形参表传入参数)
}
函数模板和函数的次序:
- 在有多个函数和函数模板名字相同的情况下,编译器处理
- 先找函数完全匹配的普通函数(未模板实例化的)
- 再找参数完全匹配的模板函数
- 再找实参经过自动类型转换之后能匹配的函数
- 否则,error
template<class T,class T2>
void Max(T a,T2 b)
{
cout<<"Yes"<<endl;
}
int main()
{
Max(5,6);
}
//匹配模板函数时,编译器不会帮你自动类型转换
//如果T a,T b
//但是,a是int,b是double就报错,T不能转变成既是int又是double的数
//T a,T2 a
//两个都是int是可以的
典型模板——Map
#include <iostream>
using namespace std;
//Map的作用是,将区间s到e这一段,通过函数指针op的处理后,拷贝到x
template<class T,class Pred>
void Map(T s,T e,T x,Pred op)//通常实例化的时候,s和e都是指针,x是指针,op是函数指针
{
for(;s!=e;++s,++x)
{
*x=op(*s);
}
}
int Cube(int x)
{
return x*x*x;
}
double Square(double x)
{
return x*x;
}
int a[5]={1,2,3,4,5},b[5];//a是原区间,b是目标区间
double d[5]={1.1,2.1,3.1,4.1,5.1},c[5];
int main()
{
//希望你能回忆起函数指针
//(*op)(double)//函数指针(函数形参表)
Map(a,a+5,b,Square);
for(int i=0;i<5;i++)
cout<<b[i]<<" ";
cout<<endl;
Map(a,a+5,b,Cube);
for(int i=0;i<5;i++)
cout<<b[i]<<" ";
cout<<endl;
Map(d,d+5,c,Square);
for(int i=0;i<5;i++)
cout<<c[i]<<" ";
cout<<endl;
}
类模板
为了快速生成相似的类,定义类模板,然后由类模板生成不同的类
例如写一个数组的类,数组里面存储类型不同,你放的东西也不同
因此可以用模板类解决
//成员变量 类似但数据类型不相同
//形式
template<class T,class T1>
//template<typename T,typename T1>//也可以
class 类模板名
{
成员函数和成员变量
};
//类模板里成员函数的写法
返回值类型 类模板名<类型参数名列表>::成员函数名(参数表)
{
......
}
//用类模板定义对象的写法:
类模板名<真实类型参数表> 对象名(构造函数实参表)
类模板——Pair类模板
#include <iostream>
using namespace std;
template<typename T1,typename T2>
class Pair
{
public:
T1 key; //关键字
T2 value;//值
Pair(T1 k,T2 v):key(k),value(v){ };
//这个重载< 用于自定义排序规则,传入参数是同类的另一个对象
//因为这个是类模板,所以应该得是类型也一样的同类
bool operator<(const Pair<T1,T2> &p)const;
};
template<class T1,class T2>
bool Pair<T1,T2>::operator<(const Pair<T1,T2>& p)const
{
return key<p.key;
//按key的大小 从小到大排序
}
int main()
{
Pair<string,int> student("name",100);
//实例化出对象,要现有对应的真实数据类型
cout<<student.key<<" "<<student.value<<endl;
}
同一个类模板的两个模板类是不兼容的
函数模板作为类模板成员
成员函数是一个函数模板
#include <iostream>
using namespace std;
template<typename T>
class A
{
public:
template<class T2>
void Func(T2 t)
{
cout<<t<<endl;
}
};
int main()
{
A<int> a;
a.Func(33);//成员函数模板化
a.Func("Hello");
}
类模板与派生
- 类模板 从 类模板派生
- 类模板 从 模板类派生
- 类模板 从 普通类派生
- 普通类 从 模板类派生
//类模板 从 类模板派生
template<typename T1,typename T2>
class A
{
T1 v1;
T2 v2;
};
template<typename T1,typename T2>
//派生下来的同时,要把基类进行实例化
//B<int,double> obj1;
//=> class B<int,double>:public A<double,int>{}
class B:public A<T2,T1>
{
T1 v3;
T2 v4;
};
template<class T>
class C:public B<T,T>
{
T v5;
};
//类模板 从模板类派生
template<typename T1,typename T2>
class A
{
T1 v1;
T2 v2;
};
template<class T>
class C:public A<int,double>
{
T v5;
};
int main()
{
C<char> obj1;
//就会生成基类A<int,double>
}
//从普通类继承,就和已被实例化的类模板一样
//普通类从模板类继承,需要保证模板类已被实例化
类模板和友元
//函数、类和类的成员函数作为类模板的友元
//则由这个模板生成的所有类,都有这些友元
template<typename T1,typename T2>
class A
{
T1 v1;
T2 v2;
public:
friend void Func1();
friend class B;
friend void B::Func();
};
//函数模板作为类模板的友元
template<typename T1,typename T2>
class Pair
{
public:
T1 key; //关键字
T2 value;//值
Pair(T1 k,T2 v):key(k),value(v){ };
bool operator<(const Pair<T1,T2> &p)const;
template<class T3,class T4>
friend ostream& operator<<(ostream& o,const Pair<T3,T4>& p);
};
//则由这个Pair模板生成的所有类,都有这个重载左移运算符的函数模板
//类模板做类模板的友元
//那么这个友元模板在使用类时,要先将其实例化
template<class T>
class B
{
private:
int v;
public:
template<class T2>
friend class A;
};
template<class T2>
class A
{
B<int> o(10);//先实例化
void Func()
{
cout<<o.v<<endl;
}
}
类模板与static成员
类模板钟同样可以定义静态成员,那么从该类模板实例化得到的所有类包含同样的静态成员
实例化不同的类,静态成员变量不同,要实例化相同的类才共用一个静态成员变量
//静态成员变量的声明:
template<> int A<int>::count=0;
template<> int A<double>::count=0;
三维数组——内部类
template <class T>
class CArray3D
{
private:
T *array;
int i,j,k;
public:
CArray3D(int _i,int _j,int _k):i(_i),j(_j),k(_k)
{
array=new T[_i*_j*_k];
}
~CArray3D()
{
delete[] array;
}
class CArray2D
{
private:
T *arrayTwo;
int k2;
public:
CArray2D(T *_arrayTwo,int _k2):arrayTwo(_arrayTwo),k2(_k2){ }
T* operator[](int j2)
{
return arrayTwo+j2*k2;
}
operator T*()
{
return arrayTwo;
}
};
CArray2D operator[](int x)
{
T* arrayTwo=array+x*j*k;
return CArray2D(arrayTwo,k);
}
};