C++ 面向对象编程7 模板
概念
模板作用强大且复杂,由于水平有限本文只做简单讲解和使用。模板的作用就是实现类型通用,降低代码的冗余度模板可以为一种算法定义适用不同类型的版本,实现机制:
1.使用类型参数摆脱类型的限制,丧失了一定的类型安全;
2.模板要实例化才能使用,实例化由编译器来实现的。
分类
函数模板
函数模板就是带类型参数的函数,函数的返回值,局部变量,形式参数都可以使用类型参数,函数模板支持类型推断(形参)。
函数模板 ---> 实例化 ---> 函数
编译
类模板
类模板就是带类型参数的类,类的成员变量,成员函数,成员类型都可以使用类型参数,类模板不支持类型推断。
类模板 ---> 实例化 ---> 类 ---> 实例化 ---> 对象
编译 运行
使用
函数模板
语法:
//函数模板定义
template<typename T/*类型参数*/....>
返回值类型 函数模板名(形参列表)
{
.......//函数中可以使用类型参数T作为类型
}
//函数模板的调用
函数模板名<类型...>(实参);//如果类型参数可以通过实参推断出来,传递的类型参数可以省略
类模板
语法:
//类模板的定义
template<typename T/*类型参数*/....>
class 类模板名{
........//类的成员可以使用类型参数T作为类型
};
//类模板的使用
类模板名<类型...> 对象名; //类模板不支持类型推断
模板的特化
如果模板对某些特殊的类型的行为需要重新定义,此时可以进行模板特化;比如创建一个用于比较大小的函数模板,无论是int型还是float型、char型都可以通用,但是对于char*(字符串),是不能使用这个模板的,所以需要对这个模板进行特化,当传入的参数是char *则调用重新定义的模板,而不是使用之前通用类型的模板,对于类模板也是一样,因为有一些类型的运算和通用模板不符也需要进行特化,语法如下:
//函数模板特化
template<>
返回值类型 函数模板名<特化的类型...>(形参列表)
{
.....//使用特化的类型
}
//类模板特化
template<>
class 类模板名<特化的类型...>{
........//类的成员使用特化类型作为类型
};
编译器在对模板实例化时,如果原模板和特化模板同时符合,优先选择特化的模板。
实验程序:
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
//函数模板
template <typename T>
T mymax(const T a, const T b)
{
return a > b ? a : b;
}
//函数模板特化
template<>
const char * mymax<const char *>(const char *str1, const char *str2)
{
return strcmp(str1, str2) > 0 ? str1 : str2;
}
//类模板
template <typename T>
class comparator{
public:
comparator(T a, T b):a(a),b(b){};
T max()
{
return a > b ? a : b;
}
T min()
{
return a > b ? b : a;
}
private:
T a;
T b;
};
//类模板特化
template<>
class comparator<const char* >{
public:
comparator(const char *str1, const char *str2):str1(str1),str2(str2){};
const char *max()
{
return strcmp(str1, str2) > 0 ? str1 : str2;
}
private:
const char *str1;
const char *str2;
};
int main()
{
const char *str1 = "abc";
const char *str2 = "efg";
cout<<"int max:"<<mymax<int>(99, 100)<<endl;
cout<<"float max:"<<mymax<float>(0.000001, 0.000002)<<endl;
cout<<"const char *:"<<mymax<const char *>(str1, str2)<<endl;
comparator<int> c1(100, 200);
cout<<"class int max:"<<c1.max()<<endl;
cout<<"class int min:"<<c1.min()<<endl;
comparator<float> c2(0.001, 0.002);
cout<<"class float max :"<<c2.max()<<endl;
cout<<"class float min :"<<c2.min()<<endl;
comparator<const char *> c3("abc", "efg");
cout<<"class const char * max:"<<c3.max()<<endl;
return 0;
}
输出结果:
类模板的成员特化
对于类模板而言,既可以进行全类特化,也可以只针对部分与类型相关的成员函数进行特化。成员特化时要保证接口的规则和原通用模板完全一致,语法如下:
template<>
返回值类型 类模板名<特化类型...>::成员函数名(形参列表)
{
........//使用特化的类型
}
实验程序:排序
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
template<typename T>
class sortor{
public:
sortor(T *a, int n)
{
this->a = new T[n];
this->n = n;
for(int i = 0; i < n; i++)
{
this->a[i] = a[i];
}
}
~sortor()
{
delete[] this->a;
}
void sort()
{
int i;
int j;
for(i = 0; i < n-1; i++)
{
bool flag = true;
for(j = 0; j < n - 1 - i; j++)
{
if (a[j]>a[j+1])
{
flag = false;
T tmp = a[j];
a[j] = a[j + 1];
a[j + 1] = tmp;
}
}
//扫描一次没有交换,说明已经排好序了
if (flag)
break;
}
}
void print()
{
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
private:
T *a; //首地址
int n; //长度
};
//成员特化
template<>
void sortor<const char *>::sort()
{
int i;
int j;
for(i = 0;i < n - 1; i++)
{
bool flag = true;
for(j = 0; j < n - 1 - i; j++)
{
if(strcmp(a[j], a[j + 1]) > 0)
{
flag = false;
const char *tmp = a[j];
a[j] = a[j + 1];
a[j + 1] = tmp;
}
}
//扫描一次没有交换,说明已经排序好了
if(flag)
break;
}
};
int main()
{
int arr[] = {5,9,6,8,4,7,1,17,2};
double d_arr[] = {5.9,6.8,4.0,7.5,3.6,8.9,2.4,9.7,1.3};
string str_arr1[] = {"ddd","ccc","bbb","eee","aaa"};
const char *str_arr2[] = {"ddd","ccc","bbb","eee","aaa"};
sortor<int> s1(arr, sizeof(arr)/sizeof(arr[0]));
s1.sort();
s1.print();
sortor<string> s2(str_arr1,sizeof(str_arr1)/sizeof(str_arr1[0]));
s2.sort();
s2.print();
sortor<const char *> s3(str_arr2, sizeof(str_arr2)/sizeof(str_arr2[0]));
s3.sort();
s3.print();
return 0;
}
输出结果:
类模板的局部特化
对于具有多个类型参数的类模板,可以只特化其中一部分类型参数,编译器优先选择特化程度最高的版本。
1.特化一部分类型参数
template<T1,T2,T3>
class xxxxx{};
//局部特化
template<T1,T2>
class xxxxx<T1,T2,int>{};
tempalte<T1>
class xxxxx<T1,char,int>{}
2.特化类型参数的特殊关系
template<T1,T2,T3>
class xxxxx{};
//局部特化
template<T1,T2>
class xxxxx<T1,T2,T2>{};
3.特化指针和数组类型
template<T1,T2,T3>
class xxxxx{};
//局部特化
template<T1,T2,T3>
class xxxxx<T1*,T2*,T3*>{};
实验程序
#include <iostream>
#include <string>
using namespace std;
//通用类模板特化
template<typename T1, typename T2, typename T3>
class trib{
public:
trib()
{
cout<<"trib<T1, T2, T3>"<<endl;
}
};
//局部特化
//1.特化某些类型参数
template<typename T1, typename T2>
class trib<T1, T2, int>{
public:
trib()
{
cout<<"trib<T1, T2, int>"<<endl;
}
};
//2.特化类型参数的关系
template<typename T1, typename T2>
class trib<T1, T2, T2>{
public:
trib()
{
cout<<"trib<T1, T2, T2>"<<endl;
}
};
//3.特化数组/指针类型
template<typename T1, typename T2, typename T3>
class trib<T1*, T2*, T3*>{
public:
trib()
{
cout<<"trib<T1*,T2*,T2*>"<<endl;
}
};
//全特化
template<>
class trib<int, char, int>{
public:
trib()
{
cout<<"Trib<int,char,int>"<<endl;
}
};
int main()
{
trib<int, double, char> t1;//通用
trib<double, char, int> t2; //局部特化
trib<double, double, int> t3; //局部特化
trib<int, double, double> t4; ////特殊关系特化
trib<int,char,int> t5; //全特化
trib<int *,double *,char *> t6; //特化数组/指针
return 0;
}
输出结果:
类模板的默认参数
1.类模板的类型参数可以有默认值,有默认值的类型参数要放到右边,如果实例化时不提供参数的类型,就使用默认值;
2.后面类型参数的默认值可以使用前面类型参数的值;
3.非类型参数也可以有默认值。
实验程序:
#include <iostream>
#include <typeinfo>
using namespace std;
//类模板的默认参数
template <typename T1=int, typename T2=char, int T3=1000>
class A{
public:
static void print_type()
{
cout<<typeid(T1).name()<<" "
<<typeid(T2).name()<<" "
<<T3<<endl;
}
};
template<typename T1, typename T2=T1>
class B{
public:
static void print_type()
{
cout<<typeid(T1).name()<<" "
<<typeid(T2).name()<<endl;
}
};
int main()
{
const int num = 10;
A<int, int, num>::print_type();
A<double, short>::print_type();
A<string>::print_type();
A<>::print_type();
B<double>::print_type();
return 0;
}
输出结果:
智能指针
将类类型传入模板,构成智能指针
实验程序:
#include <iostream>
using namespace std;
class A{
public:
A(){cout<<"A()"<<endl;}
~A(){cout<<"~A()"<<endl;}
void show()
{
cout<<"showA()"<<endl;
}
};
//模板化指针对象
template<typename T>
class myauto_ptr{
public:
myauto_ptr(T *p=NULL):pdata(p)
{
}
myauto_ptr(myauto_ptr<T> &t) //自定义类类型将调用这个构造函数
{
this->pdata = t.data;
t.pdata = NULL;
}
~myauto_ptr()
{
if(this->pdata)
{
delete this->pdata;
this->pdata = NULL;
}
}
//重载->运算符
T *operator->()
{
return this->pdata;
}
//重载*运算符
T& operator*()
{
return *this->pdata;
}
private:
//要管理的指针
T *pdata;
};
int main()
{
int *p_int = new int(10);
myauto_ptr<int> pi(p_int);
cout<<*pi<<endl;
A *p_a = new A;
myauto_ptr<A> pa(p_a);
pa->show();
(*pa).show();
return 0;
}
输入结果:
待补充:
模板的继承
模板的模板成员
模板的模板参数