泛型编程:利用模板的一种技术进行实现。
(一)基本概述
模板是建立通用的模具,大大提高复用性。模板不可以直接使用,它只是一个框架。通用但不是万能。
C++中的模板分为函数模板和类模板。
函数模板的语法:建立一个通用的函数,其返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。template<typename T>
template-->声明创建模板
typename-->表面其后面的数据类型是一种数据类型,可以用class代替。
T-->通用的数据类型,名称可以替换,通常为大写字母
void swapint(int &a,int &b)
{
int t=a;
a=b;
b=t;
}
void swapdouble(double &a,double &b)
{
double t=a;
a=b;
b=t;
}
//函数模板:
template<typename T>//声明一个模板,告诉编译器后面代码中紧跟着的T不要报错。T是一个通用的数据类型
void myswap(T &a,T &b)
{
T temp=a;
a=b;
b=temp;
}
利用函数模板交换:
1)显示指定类型
myswap<int>(a,b);
2) 自动类型推导
myswao(a,b);
函数模板注意事项:
1)自动类型推导,必须推导出一致的数据类型T才可以使用
2)模板必须要确定T 的数据类型,才可以使用
template<class T>==template<Typename T>
void myswap(T&a,T&b)
{
T temp=a;
a=b;
b=temp;
}
void test01()
{
int a=10;
int b=20;
myswap(a.b);//自动类型匹配
};
template<class T>
void func()
{
cout<<"调用”<<endl;
}
void test01{
func<int>();//模板必须要确定数据类型,才可以使用
}
(二)函数模板案例:利用模板对不同类型的数据进行排序
template<typename T>
void mysort(T arr[].int len)
{
for(int i=0;i<len-1;i++)
{
max=i;
for(j=0;j<len;j++)
{
if(a[j]>max) max=j;
}
if(max!=i)
swap(arr,max,i);
}
}
普通函数和函数模板的区别:
普通函数使用的时候可以发生自动类型转换(隐式类型转换)
函数模板调用的时候,如果利用自动类型推导,不会发生 隐式类型转换
如果利用显示指定类型的方式,可以发生隐式类型转换
int add(int a,int b)
{
return a+b;
}
void test{
int a=10;
char c='c';
cout<<add(a,c)<<endl;//输出109
}
template<class T>
T myadd(T a,T b)
{
return a+b;
}
void test02()
{
cout<<myadd(a,c)<<endl;//err
cout<<myadd<int>(a,c)<<endl;//right
}
调用规则:
如果普通函数和函数模板都可以实现,优先使用普通函数。
(当只有一个普通函数的声明的时候会报错)
都可以通过空模板参数列表来强调函数模板。
myPrint<>(a,b);
函数模板也可以发生重载。
如果函数模板可以产生更好的匹配,优先调用函数模板。
void myprint(int a,int b)
{
cout<<“调用的普通函数”<<endl;
}
template<class T>
void myprint(T a,T b)
{
cout<<"调用的模板"<<endl;
}
(三)模板的局限性
比如有的时候传入的是类似person的具体数据类型,无法正常运行。C++为此提供模板的重载,为这些特定的类型提供具体的模板。
class person{
public:
person(string name,int age)
{
this->m_name=name;
this->m_age=age;
}
string m_name;
int m_age;
};
template<class T>
bool mycompare(T &a,T &b)
{
if(a==b)
return ture;
else
return false;
}
//利用具体化的person版本实现代码,具体化优先调用
template<>bool mycompare(person &p1,person &p2)
void test02()
{
person p1("mm",18);
person p2("tt",19);
bool ret=mycompare(p1,p2);
if(ret)
{
cout<<"p1==p2"<<endl;
}
else
{
cout<<"p1!=p2"<<endl;
}
}
(四)类模板:
作用:建立一个通用的类,类中的成员和类型不具体确定,用一个虚拟的类型来代表。
template<Typename T>
类
template--声明创建模板
typename--表明其后面的符号是一种数据类型,可以用class代替
T--通用的数据类型,名称可以替换,通常为大写字母
template<class NameType,class AgeType>
class person
{
public:
person(NameType name,AgeType age)
{
this->m_age=age;
this->m_name=name;
}
void showperson()
{
cout<<this->m_Name<<endl;
}
NameType m_name;
AgeType m_age;
};
void test01()
{
person<string,int>p1("孙悟空",999);//只能用显示指定类型
p1.showperson();
}
类模板和函数模板的区别:
类模板中没有自动类型推导的使用方式+类模板在模板参数列表中可以有默认参数
class person
{
public:
person(NameType name,AgeType age=int)
{
this->m_age=age;
this->m_name=name;
}
void showperson()
{
cout<<this->m_Name<<endl;
}
NameType m_name;
AgeType m_age;
};
类模板中成员函数的创建时机:普通类中的成员函数一开始就创建了,类模板中的成员函数在调用的时候再创建。
(五)类模板对象作为参数:
template<class T1,class T2>
class person
{
public:
person(T1 name,T2 age)
{
this->m_Name=name;
this->m_age=age;
}
void showperson()
{
cout<<this->m_Name<<this->m_age<<endl;
}
T1 m_Name;
T2 m_age;
};
void test01()
{
person<string,int>p("孙悟空",100);
showperson(p);
}
1)指定传入的类型——直接显示对象的数据类型
void printperson(Person<string,int>&p)
{
p.showperson();
}
2)参数模板化——将对象中的参数变为模板进行传递
template<class T1,class T2>
void printperson(Person<T1,T2>&p)
{
p.showperson();
cout<<"T1的类型为"<<typeid(T1).name()<<endl;
}
3)整个类模板化——将这个对象类型 模板化进行传递
template<class T>
void printperson(T &p)
{
p.showperson();
}
(六)类模板与继承:
1)当子类继承的父类是一个类模板的时候,子类在声明的时候,要指出父类中T的类型
template<class T>
class base
{
T m;
};
class son:public base<int>
{
};
2)如果不确定,编译器无法为子类分配内存
3)如果想要灵活地指出父类中T的类型,子类也要变成类模板
template<class T1,class T2>
class son2:public base<T2>
{
T1.obj;
};
类模板成员函数的类外实现:
template<class T1,class T2>
class person
{
public:
person(T1 name,T2 age);
void showperson();
T1 m_Name;
T2 m_age;
};
template<class T1,class T2>
person<T1,T2>:: person(T1 name,T2 age)
{
this->m_Name=name;
this->m_age=age;
}
void person<T1,T2>:: showperson()
{
cout<<this->m_Name<<this->m_age<<endl;
}
(七)类模板的分文件编写
问题:类模板的成员函数创建时机是在调用阶段,会导致分文件编写的时候链接不到
解决方法:
直接包含cpp源文件//不能用.h
将声明和实现(.h和.cpp)写到同一个文件中,并且更改后缀名为hop.hop是约定的名称而不是强制的。
类模板和友元:
掌握类模板配合友元函数的类内和类外实现。
全局函数类内实现:直接在类内声明友元 全局函数的类外实现:需要提前让编译器直到全局函数的存在。
//全局函数 类内实现
friend void showperson(person<T1,T2>P)
{
cout<<p.m_name<<p.m_age<<endl;
}
类内:加空模板参数列表
friend void showperson<>(person<T1,T2>p)
全局函数类外实现
template<class T1,class T2>
void showperson(person<T1,T2>,p)
{
cout<<p.m_name<<endl;
}