C++泛型程序设计与模板
Generic Programming
C++为了提高程序的可重用性,采用了两种方法,继承和泛型程序设计.
review:
函数模板 Function Template
举个例子,你想交换double double又想交换 int int 你就要写两个普通函数.非常的麻烦.
原理:函数模板会在实例化的时候自动生成对应的函数,因此在空间上的开销和普通函数是一个鬼样的
下面写一个非常简单的函数模板,回顾一下
#include <iostream>
#include <vector>
using namespace std;
template <class T>
void Swap(T& a, T& b)
{
T tmp;
tmp=a;
a=b;
b=tmp;
}
int main()
{
double a=1.00 ,b =8.00;
int a1=1 ,b1=2;
Swap(a1,b1);
Swap(a,b);
return 0;
}
下面再回顾回顾显示调用
#include <iostream>
using namespace std;
template <class T>
T Fun(int n)
{return 1+n;}
int main()
{
cout<<Fun<double>(4)/2;//显示的告诉编译器要doule的
return 0;
}
再来看看调用的规则和匹配顺序
1.优先调用普通函数
2.没有普通函数调用函数模板
3.都没有就调用自动类型转换后可以匹配的函数
4.全部找不到后,编译器就呱呱报错了
注意:函数模板也是可以重载的
那个class写成typename也可以,我就喜欢写class,字少。。。
#include <iostream>
using namespace std;
template <class T>
void Fun(T n)
{
cout<<"Template Function"<<endl; }
void Fun(int n)
{
cout<<"Fuction"<<endl;
}
int main()
{
int a=1;
double b=1.00;
Fun(a);
Fun(b);
Fun<>(a);
return 0;
}
/*output:
Fuction
Template Function
Template Function*/
那下面就不厌其烦的写个经典的map吧
#include <iostream>
using namespace std;
template<class T,class Pred>
void Map(T s,T e,T x,Pred op)
{
for(;s!=e;++s,++x)
*x=op(*s);//最后一个参数显然是一个函数指针
//ex:实例化double(*op)(double)
}
//体积
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];
double d[5]={1.1,2.1,3.1,4.1,5.1},c[5];
int main()
{
Map(a,a+5,b,Square);
for(size_t i=0;i<5;i++)
cout<<b[i]<<" ";
cout<<endl;
Map(a,a+5,b,Cube);
for(size_t i=0;i<5;i++)
{
cout<<b[i]<<" ";
}
cout<<endl;
Map(d,d+5,c,Square);
for(size_t i=0;i<5;++i)
cout<<c[i]<<" ";
cout<<endl;
return 0;
}
好不容易好啰嗦呀,终于把函数模板讲完了
下面讲一下类模板吧
类模板的原理
插入一个关于宏的博客https://blog.csdn.net/pirlck/article/details/51254590
为了多快好省的定义一批相类似的类,可以定义类模板生成不同的类
然后由类模板生成不同的类
下面也来举个例子
来个经典的Pair类
#include <iostream>
#include <string>
using namespace std;
template<class T1,class 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;//这里的"大小”还是比较正常的
}
int main()
{
Pair<string,int>student("Tom",19);
//实例化了Pair<string,int>
cout<<student.key<<" "<<student.value;
return 0;
}
注意:同一个类模板的两个模板类是不兼容的
成员函数也可以是函数模板,看下面示例
#include <iostream>
#include <string>
using namespace std;
template< class T>
class A
{
public:
template<class T2>
void Fun(T2 t)
{
cout<<t<<endl;//成员函数模板
}
};
int main()
{
A<int >a;
a.Fun('K');//char
a.Fun("hello");//char *
return 0;
}
ok,好了,下面看最经典的可边长数组类模板,顺便回顾以下重载运算符
#ifndef ARRAY_CLASS
#define ARRAY_CLASS
#include<iostream>
#include<stdlib.h>
#ifndef NULL
const int NULL = 0;
#endif//NULL
using namespace std;
enum ErrorType
{
invalidArraySize, memoryAllocationError, indexOutOfRange
};
//枚举错误信息
char *errorMsg[] =
{
"Invalid array size", "Memory allocation error", "Invalid index:"
};
//数组类模板
template <class T>
class Array
{
private:
T* alist;
int size;
//错误处理函数
void Error(ErrorType error, int badIndex = 0)const;
public:
Array(int sz = 50); //构造函数
Array(const Array <T> & A); //拷贝构造函数
~Array(void); //析构函数
//重载“=”使数组对象可以整体赋值
Array<T>&operator = (const Array <T>&rhs);
//重载“[]”使Array对象可以起到C++普通函数的作用
T&operator[](int i);
operator T *(void)const;
int ListSize(void)const; //取数组的大小
void Resize(int sz); //修改数组的大小
};
//Array.h中的函数实现
//#include"Array.h"
template <class T>//!!!!!!!记住凡是模板都要写这个东东!!!!!!!!!!!!!!!!!!!!!
void Array<T>::Error(ErrorType error, int badIndex)const
{
cout << errorMsg[error]; //根据错误类型,输出相应的错误信息
if(error == indexOutOfRange)
cout<<badIndex;
cout<<endl;
exit(1); //执行这个函数后,程序推出,系统回收资源
}
//构造函数
template <class T>
Array<T>::Array(int sz)
{
if(sz <= 0) //sz为数组大小(元素个数)。若小于0,则输出错误信息
Error(invalidArraySize);
size = sz; //将元素个数赋给变量size
alist = new T[size]; //动态分配size个T类型的元素空间
if(alist == NULL) //如果分配资源不成功
Error(memoryAllocationError);
}
//析构函数
template <class T>
Array<T>::~Array(void)
{
delete []alist;
}
//拷贝构造函数
template <class T>
Array<T>::Array(const Array<T>&X)
{
//从对象X取得数组大小,并赋值给当前对象的成员
int n = X.size;
size = n;
alist = new T[n]; //动态分配n个T类型的元素空间
if(alist == NULL) //如果分配内存不存在,输出错误信息
Error(memoryAllocationError);
//从对象X复制数组元素到本对象
T *srcptr = X.alist; //X.alist是对象X的数组首地址
T *destptr = alist; //alist是本对象中的数组首地址
}
//重载“=”运算符,将对象rhs赋值给本对象,实现对象之间的整体赋值
template <class T>
Array<T>&Array<T>::operator = (const Array<T>& rhs)
{
int n = rhs.size; //取rhs的数组的大小
//如果本对象中数组大小与rhs不同,则删除数组原有内存,然后重新分配
if(size!=n)
{
delete[]alist; //删除数组原有内存
alist = new T[n]; //重新分配n个元素的内存
if(alist == NULL) //如果分配不成功,输出错误信息
Error(memoryAllocationError);
size = n; //记录本对象的数组大小
}
//从rhs中向本对象复制元素
T* destptr = alist;
T* srcptr = rhs.alist;
while(n--)
*destptr ++ = *srcptr ++;
return *this;
}
//重载下标操作符,实现与普通数组一样通过下标访问元素,并具有越界检测功能
template <class T>
T& Array<T>::operator[](int n)
{
//检查下标是否越界
if(n < 0 || n > size - 1)
Error(indexOutOfRange, n);
//返回下标为n的数组元素
return alist[n];
}
//重载指针转换操作符,将Array类的对象名转换为T类型的指针
//指向当前对象中的私有数组。
//因而可以像使用普通数组首地址一样使用Array类的对象名
template <class T>
Array<T>::operator T*(void)const
{
//返回当前对象中私有数组的首地址
return alist;
}
template <class T>
int Array<T>::ListSize(void)const
{
return size;
}
//将数组大小改为sz
template <class T>
void Array <T>::Resize(int sz)
{
//检查是否sz<=0
if(sz <= 0)
Error(invalidArraySize);
//如果指定的大小与原有大小一样,什么也不做
if(sz == size)
return;
//申请新的数组内存,并测试是否申请成功
T* newlist = new T[sz];
if(newlist == NULL)
Error(memoryAllocationError);
//将sz与size中较小的一个赋值给n
int n = (sz <= size)?sz:size;
//将原有数组中的前n个元素复制到新的数组中
T* srcptr = alist; //原数组alist的首地址
T* destptr = newlist; //新数组newlist的首地址
while(n--)
*destptr ++ = *srcptr ++;
//删除原数组
delete[] alist;
//使alist指向新数组,并更新size;
alist = newlist;
size = sz;
}
#endif//ARRAY.CLASS
类模板当中的非类型参数
template<class T,int size>
class A
//实例化的时候可以
A<double,40>a
类模板和继承
example
1.模板类从类模板中派生
#include <iostream>
#include <string>
using namespace std;
template<class T1,class T2>
class A
{
T1 v1;
T2 v2;
};
//继承
template <class T1,class T2>
class B:public A<T2,T1>
{
T1 v3,T2 v4;
};
int main()
{
B<int,double>obj1;//同时有A<double,int>
return 0;
}
2.类模板从模板类派生
3.类模板从普通类派生
4.普通类从模板类派生
类模板和友元
这里的关系复杂无比呀,我无力写下去了,就借鉴以下别人的吧
友元包括友元函数和友元类
类模板和友元
注意
他们的关系,友元是不是所有类的友元捏
还有就是记得写!!!部分
class B:public A<T2,T1>
{
int v;
public:
template<class T>//!!!
friend void Print(T &p);
};
template<class T>
void Print(T &p)
{
cout<<p.v;
}
类模板和静态成员变量
主要就是初始化的问题
不同的模板的静态成员变量毫不相干的
初始化方法
//对于class A
template <class T>
class A
{
static int count;
}
template< > int A <int>::count=0;//!!!虽然却是有的ide不用写template< >,建议写上
静态成员函数
哦哦对了,注意 < > 习惯打上个空格,以防有的编译器当成左移或右移运算符 🤪
学会程序和算法,走遍天下都不怕
清迈白庙