泛型程序设计

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< >,建议写上

静态成员函数

在这里插入图片描述
哦哦对了,注意 < > 习惯打上个空格,以防有的编译器当成左移或右移运算符 🤪
 

学会程序和算法,走遍天下都不怕

在这里插入图片描述
清迈白庙

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值