泛型(Generic Programming)即是指具有在多种数据类型上皆可操作的含意。泛型编 程的代表作品STL是一种高效、泛型、可交互操作的软件组件。
泛型编程最初诞生于 C++中,目的是为了实现C++的 STL(标准模板库)。其语 言支持机制就是模板(Templates)。模板的精神其实很简单:参数化类型。换句话说, 把一个原本特定于某个类型的算法或类当中的类型信息抽掉,抽出来做成模板参数 T。
所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。
1.函数模板
在函数模板未引入之前,对同一功能函数的不同类型参数实现只能通过函数重载实现,而模板则是把这个过程泛化
语法格式
template<typename T>
template<class T>
template<typename 类型参数表>
返回类型 函数模板名(函数参数列表)
{
函数模板定义体
}
-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐
#include <iostream>
using namespace std;
//模板就是对参数类型的参数化
//此时编译器不知道T是什么
//告诉编译器T是一个类型的泛化
template <typename T>//告诉编译器T是一个泛化类型
template <class A> //上下两种写法是等价的!
void mySwap(T &a,T &b)
{
T temp = a;
a = b;
b = temp;
}
template <typename T>//每个函数都要声明一次!
void print(T &a,T &b)
{
cout<<"a = "<<a<<" b = "<<b<<endl;
}
int main(void)
{
int a = 10;
int b = 20;
cout<<"交换之前"<<endl;
//<>在调用模板函数的时候,要告诉编译器T泛化的类型,具体到底是什么类型
print<int>(a,b);
mySwap<int>(a,b);
cout<<"交换之后"<<endl;
print<int>(a,b);
char x = 'x';
char y = 'y';
cout<<"交换之前"<<endl;
print<char>(x,y);
mySwap<char>(x,y);
cout<<"交换之后"<<endl;
print<char>(x,y);
return 0;
}
template 是语义是模板的意思,尖括号中先写关键字 typename 或是class ,后 面跟一个类型 T,此类即是虚拟的类型。至于为什么用 T,用的人多了,也就是 T 了。
排序模板函数练习
//
#include <iostream>
using namespace std;
template<typename T>
int array_sort(T *array,int num)
{
T temp;
//len = sizeof(array)/sizeof(array[0]);
//不能再这里写,这里的array已经退化成一个指针了,len只能是1
for (int i = 0; i < num; ++i)
{
for (int j = i+1; j < num ; ++j)
{
if (array[j] > array[i])
{
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
return 0;
}
//打印数组的方法
template<typename T>
void print_array(T *array,int num)
{
for (int i = 0; i < num; ++i)
{
cout<<array[i]<<" ";
}
cout<<endl;
}
int main(void)
{
char str[] = "dsagfsdhdsgbdfbgfdujhtfyhrd";
int len = strlen(str);
array_sort<char>(str,len);
print_array<char>(str,len);
cout<<"-------------------------"<<endl;
//排序一个Int数组
int array[] = {3,24,21,51,2,5,55,21};
len = sizeof(array)/sizeof(array[0]);
array_sort<int>(str,len);
print_array<int>(str,len);
return 0;
}
- 当函数模板和普通函数都符合调用时,优先选择普通函数
- 若显示使用函数模板,则使用<> 类型列表
- 如果函数模板产生更好的匹配 使用函数模板
2.类模板
类模板与函数模板的定义和使用类似,我们已经进行了介绍。有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同,所以将类中的类型进行泛化。
//基本语法
template<typename T>
class A
{
}
//----------------------------------------------------------
#include <iostream>
using namespace std;
template<typename T>
class A
{
public:
A(int a){
this->a =a;
}
void printA(){
cout<<"a = "<<a<<endl;
}
private:
T a;
};
//继承模板类,直接继承已经实例化的模板类
//这么继承完之后的B类,是普通类
class B:public A<int>
{
public:
B(int a,int b):A(a){//A(a)加不加<int>都无所谓!
this->b = b;
}
void printB(){
cout<<"b = "<<b<<endl;
}
private:
int b;
};
//C类继承的是一个模板类,没有实例化,此时C类仍然是一个模板类
template<typename T>
class C:public A<T>
{
public:
C(T a,T c):A(a){
this->c = c;
}
void printC(){
cout<<"c = "<<c<<endl;
}
private:
T c;
};
//把模板类当参数
template<typename T>
void func(A<T> &a)
{
a.printA();
}
//普通函数
void func2(A<int> &a)
{
a.printA();
}
int main(void)
{
//普通实例化
A<int> a(10);
a.printA();
A<double> b(30.0);
b.printA();
cout<<"-----------------------"<<endl;
//类作为参数
func<int>(a);
func2(a);
cout<<"-----------------------"<<endl;
//模板类继承
B b(100,200);
b.printB();
C<int> c(1000,2000);//c是一个模板类
c.printC();
return 0;
}
2.1类模板的实现(函数体写在类外 有坑!)
//-------------------------------------Complex.h-------------------------------------------------
#include <iostream>
using namespace std;
#ifndef __COMPLEX_H__
#define __COMPLEX_H__
template<class T>
class Complex;
template<class T>
Complex<T> mySub(Complex<T> &c1,Complex<T> &c2);
//前置声明发现缺了Complex,又要前置声明Complex,所以很不建议滥用友元!
template<typename T>
class Complex
{
public:
Complex()
Complex(T a,T b)
void printComplex(){
cout<<"("<<a<<"+"<<b<<"i)"<<endl;
}
Complex operator+(Complex &another);
Complex operator-(Complex &another);
//如果给一个模板类提供一个友元函数(在外部定义),例如<< >>重载
//要在这个重载符号和参数列表之间添加<T>
friend ostream & operator<< <T>(ostream &os,Complex<T> &c);
//滥用友元 调用就报错了,只能前置声明了!!!
//滥⽤用友元函数,本来可以当成员函数,却要⽤用友元函数
//如果说是⾮非<< >>在模板类中当友元函数
//在这个模板类之前声明这个函数
friend Complex<T> mySub<T>(Complex<T> &c1,Complex<T> &c2);
//最终的结论,模板类不要轻易写友元函数,要写的就写<<和>>。
#if 0
//或者可以在内部定义,就可以省去<T>
friend ostream & operator<<(ostream &os,Complex<T> &c){
os<<"("<<c.a<<"+"<<c.b<<"i)"<<endl;
return os;
}
#endif
private:
T a;
T b;
}
#endif
//-------------------------------------Complex.hpp-------------------------------------------------
#include "Complex.h"
template<class T>
Complex<T>::Complex()
{
}
template<class T>
Complex<T>::Complex<T>(T a,T b)
{
this‐>a = a;
this‐>b = b;
}
template<typename T>
Complex<T> Complex<T>::operator+(Complex<T> &another)
{
Complex temp(this->a+another.a,this->b+another+another.b);
return temp;
}
template<typename T>
Complex<T> Complex<T>::operator+(Complex<T> &another)
{
Complex temp(this->a - another.a , this->b - another+another.b);
//这些构造可以不加<T>,因为在类内已经声明了!和下面的mySub完全不一样
return temp;
}
template<typename T>
ostream & operator<<(ostream &os,Complex<T> &c)
{
os<<"("<<c.a<<"+"<<c.b<<"i)"<<endl;
return os;
}
template<typename T>
Complex<T> mySub(Complex<T> &c1,Complex<T> &c2)
{
Complex<T> temp(c1.a - c2.a ,c1.b - c2.b);//这里构造函数记得加<T>
return temp;
}
//-------------------------------------main.cpp-------------------------------------------------
#include <iostream>
#include "Complex.h"
//#include "Complex.cpp" //引入cpp文件之后才能运行,因为二次编译的缘故!
//但是因为cpp文件的封装性,不能够引入
#include "Complex.hpp"//改名!!!把cpp尾缀改成hpp
using namespace std;
//模板类的方法的实现不能够用多文件编写
//如果实现多文件 由于二次编译 真正地实现体是在cpp文件定义的,需要引入cpp头文件
int main(void)
{
Complex<int> c1(10,20);
c1.printComplex();
Complex<int> c2(1,2);
Complex<int> c3;
c3 = c1 + c2;
c3.printComplex();
Complex<int> c4;
c4 = c1 - c2;
//重构<<
cout<<c4;//报错了,这里有个c++编译器的坑
Complex c5;
c5 = mySub(c1,c2);//报错!!!//改完之后可以了,不建议滥用友元!
return 0;
}
- 模板类不要轻易使用友元函数。
- 由于二次编译,模板类在.h在第一次编译之后,并没有最终确定类的具体实现,只是编译器的词法校验和分析。在第二次确定类的具体实现后,是在.hpp文件生成的最后的具体类,所以main函数需要引入.hpp文件。
综上:引入hpp文件一说也是曲线救国之计,所以实现模板方法建议在同一个文件.h中完成
3.模板类中的static
#include <iostream>
using namespace std;
template <typename T>
class A
{
public:
private:
T value;
static T a;
};
template <typename T>
T A<T>::a = 0;//类中的静态成员需要在类的外部进行初始化
int main(void)
{
//1.模板类通过二次编译根据调用的代码生成了两个不同的类A
//一个是A<int> 一个是A<char>
A<int> a1,a2,a3;
A<char> b1,b2,b3;
//a1 b1不是同一种类,所以static静态变量不是共用的!
//证明:
A<int>::a = 20; //改变A<int>的静态成员
A<char>::a = 'X'; //改变A<char>的静态成员
cout<<"a1:a="<<a1.a<<endl;//20
cout<<"b1:a="<<b1.a<<endl;//X
//a1 a2 a3是同一个类,静态变量共用的,b1 b2 b3同理!
cout<<"a1:a="<<a2.a<<endl;//20
cout<<"b1:a="<<b2.a<<endl;//X
cout<<"a1:a="<<a3.a<<endl;//20
cout<<"b1:a="<<b3.a<<endl;//X
return 0;
}
4.练习 实现一个模板数组类
1.请设计一个数组类模板(MyVector),完成对int、char、Teacher类型元素的管理
2.需要实现构造函数、拷贝构造函数、<<、[]、=操作符
//自定义一个模板数组类
//1.该数组能够存放int double 还有自定义类型
//2.[] array[i] = ??
//3.cout<<array
//4.array1 = array2
//5.array1(array2)
//6.array[i] = array[j]
#ifndef __MYVECTOR_H__
#define __MYVECTOR_H__
#include <iostream>
using namespace std;
template<typename T>
class MyVector //数组:插入、删除、修改、查找
{
public:
MyVector(); //无参构造
MyVector(int len); //有参构造
MyVector(const MyVector &another);//拷贝构造
T & operator[](int index);
MyVector & operator=(const MyVector &another);
friend ostream &operator << <T>(ostream &os,MyVector<T> &vector);
#if 0 //注意有两种写法
friend ostream &operator <<(ostream &os,MyVector<T> &vector){
for (int i = 0; i < vector.len; ++i)
{
os<<vector[i]<<" ";//[]重载了,不然就是vector.a[i]了
}
os<<endl;
return os;
}
#endif
~MyVector();
private:
T* a;
int len;
}
#endif
------------------------------------------------MyVector.hpp-----------------------------------
#include "MyVector.h"
template<typename T>
MyVector<T>::MyVector(){
this->a = NULL;
this->len = 0;
}
template<typename T>
MyVector<T>::MyVector(int len){
this->len = len;
if (this->a = NULL)this->a = new T[len];//在堆上连续开辟了sizeof(T)*len的空间
}
template<typename T>
MyVector<T>::MyVector(const MyVector &another)
{
// if (this == another)return;
// delete[] this->a;
this->len = another->len;
this->a = new T[this->len];
for(int i = 0; i < another.len;i++){
this->a[i] = another.a[i];//如果存储对象,那么这个=号也是要重载实现深拷贝的!
}
}
template<typename T>
T & MyVector::operator[](int index){
return this->a[index];
}
template<typename T>
MyVector<T> & MyVector::operator=(const MyVector &another){
if (this == another)return;
if(this->a != NULL)delete[] this->a;
this->len = another.len;
this->a = new T[this->len];
for(int i = 0; i < another.len;i++){
this->a[i] = another.a[i];
}
return *this;
}
template<typename T>
ostream & operator <<(ostream &os,MyVector<T> &vector){//<< <T>不是在定义上面写,而是在声明处写
for (int i = 0; i < vector.len; ++i)
{
os<<vector[i]<<" ";//[]重载了,不然就是vector.a[i]了
}
return os;
}
MyVector::~MyVector(){
if (this->a != NULL)
{
delete[] this->a;
this->a = NULL;
this->len = 0;
}
}
--------------------------------------Teacher.h------------------------------
class Teacher
{
public:
Teacher(){
this->name = NULL;
this->id = 0;
}
Teacher(int id,char *name)
{
this->id = id;
int len = strlen(name);
this->name = new char[len + 1];
strcpy(this->name,name);
}
Teahcer(const Teahcer &t){
this->id = id;
int len = strlen(t.name);
this->name =new char[len + 1];
strcpy(this->name,t.name);
}
Teahcer & operator = (const Teahcer &t){
if (this->name != NULL)
{
delete[] this->name;
this->name = NULL;
this->id = 0;
}
this->id = t.id;
int len = strlen(t.name);
this->name = new char[len + 1];
strcpy(this->name,t.name);
return *this;
}
void printTeacher(){
cout<<"id = "<<this->id<<",name = "<<this->name<<endl;
}
friend ostream & operator <<(ostream &os,Teahcer &t);
~Teahcer(){
if (this->name != NULL)
{
delete[] this->name;
this->name = NULL;
this->id = 0;
}
}
private:
int id;
char *name;
}
ostream & operator <<(ostream &os,Teahcer &t)
{
t.printTeacher();
return os;
}
--------------------------------------main.cpp------------------------------
#include <iostream>
#include "MyVector.h"
#include "MyVector.hpp"
using namespace std;
void test1(){
MyVector<int> myVector1(10);
for (int i = 0; i < 10; ++i)
{
myVector1[i] = i + 10;
}
cout<<"vector1"<<endl;
cout<<myVector1<<endl;
MyVector<int> myVector2(myVector1);//拷贝构造
cout<<"vector2"<<endl;
cout<<myVector2<<endl;
MyVector<int> myVector3;
myVector3 = myVector2;//=重载
cout<<"vector3"<<endl;
cout<<myVector3<<endl;
}
void test2(){
//创建一些Teacher对象
Teahcer t1(5,"zhang3");
Teahcer t2(1,"zhang43");
Teahcer t3(2,"zhang5");
Teahcer t4(3,"zhang6");
Teahcer t5(4,"zhang7");
MyVector<Teahcer> myVector1(5);
myVector1[0] = t1;//myVector.opetator[](0) = t1 和 teacher.operator=(t1)
myVector1[1] = t2;
myVector1[2] = t3;
myVector1[3] = t4;
myVector1[4] = t5;
for (int i = 0; i < 5; ++i)
{
cout<<myVector1[i];
}
}
int main(void)
{
test1();
test2()
return 0;
}