类模板基础
- 1.类模板的基本概念
- 2.类模板的分文件编写
- 3.类模板的嵌套
在往节内容中,我们详细介绍了函数模板,这节开始我们就来聊一聊类模板。C++中,类的细节远比函数多,所以这个专题也会更复杂。
1.类模板的基本概念
和函数模板一样,类也可以有处理不同类型变量的能力。定义一个类模板需要使用的语句与定义函数模板类似:
template <class T1, class T2>
再次强调,尽管typename关键字和class关键字都可以定义通用变量,但通常函数中的通用变量使用typename定义,类中则使用class定义。定义后的通用变量名可以出现在类中的任何位置,比如我们写一个简单的类:
template <class T1, class T2>
class AA
{
public:T1 m_a;T2 m_b;AA(){} // 不负责任的构造函数AA(T1 a, T2 b):m_a(a),m_b(b){} T1 get_a() // 打印并返回a的值{cout << m_a << endl;return m_a;}T2 get_b() // 打印并返回b的值{cout << m_b << endl;return m_b;}
};
定义一个类模板与定义函数模板类似,但调用方法有所不同。因为C++不能自动判定传参的类型,所以我们调用时必须指定T1和T2的具体类型:
int main()
{AA<int, double> a;a = { 20,3.3 };a.get_a();a.get_b();return 0;}// 输出为:20// 3.3
此外,我们还可以给通用类行指定默认类型,类似于python中的默认参数。将定义AA类通用参数的代码换成:
template <class T1, class T2=int>
这样在调用函类的话就可以这样写:
int main()
{AA<int> a(20,3.3);AA <int,double> b (20,3.3);a.get_a();a.get_b();b.get_a();b.get_b();return 0;
}
// 输出为:20
// 3
// 20
// 3.3
从这个例子中也可以看出,这个默认值也是可以手动修改的。C++11以上标准的函数模板实际上也接受这种制定通用类型的默认值操作,但实际意义不大。
我们也可以用new关键字来创建模板类对象:
int main()
{AA<int,double> *a=new AA<int>(20,3.3);a->get_a();a->get_b();delete a;return 0;
}
需要注意,使用模板类的话类名就不是AA了,而是AA<int,double>。
2.类模板的分文件编写
与函数模板一样,类模板也是在用到的时候才会被创建成具体函数。如果需要将类的声明和定义分开,那么同函数模板一样,这个声明通用变量的语句也要跟着定义。如果想要份文件编写,类模板也要整体放倒头文件中:
这个例子中,我们有一个Student具体类和AA类模板,其中类模板的属性声明和定义是分开的,请大家仔细观察一下。
可能有小伙伴好奇这样的结果,其实原因不难理解,C++是分文件进行编译的,所以头文件和源文件乍看之下都没有错,编译器也是可以正常通过的。但是对于具体函数和具体类而言,放在源文件中的函数定义会在编译过程中被创建成具体内容与头文件形成连接。而模板不会,它只会在被调用到的时候才能生具体函数,因此编译的过程中模板函数和类模板的方法并不会与头文件形成有效的链接,所以调用也就会出错了。解决这个问题当然就是不让编译器去源文件中形成连接,而是在头文件中就放好需要的内容。
3.类模板的嵌套
在开始这部分的讲述之前,我们先看一下使用类模板实现的两种数据结构——数组和栈。了解了这部分内容,我们来利用静态数组和栈做一个类模板的嵌套使用。所谓嵌套,就是模板中存放着模板,这种方式在生活中很常见,把栈模板的元素设置成数组模板,就是一个典型案例。栈模板和数组模板的代码不需要动,我们在主函数中嵌套两个模板:
// 数组的默认大小Arraysize设置为3
int main()
{Stack<Array<string>> vs(2); // 构建一个以数组为元素的栈Array<string> aa,bb; // 数组模板,为压栈准备aa[0]="ZhangSan";aa[1]="LiSi";aa[2]="WangWu";vs.push(aa);aa[0]="ZhaoLiu";aa[1]="SunQi";aa[2]="ShenBa";vs.push(aa);while(vs.pop(bb)){for(int i=0;i<3;i++){cout<<bb[i]<<" ";}cout<<endl;}
}
// 输出为:ZhaoLiu SunQi ShenBa
// ZhangSan LiSi WangWu
函数模板的嵌套有一种自己嵌套自己的特殊用法,即递归。我们可以用这种方法做一个二维数组:
int main()
{Array<Array<string>> vs;Array<string> aa;aa[0]="ZhangSan";aa[1]="LiSi";aa[2]="WangWu";vs[0]=aa;aa[0]="ZhaoLiu";aa[1]="SunQi";aa[2]="ShenBa";vs[1]=aa;aa[0]="WuJiu";aa[1]="0";aa[2]="0";vs[2]=aa;for (int j=0;j<3;j++){for(int i=0;i<3;i++){cout<<vs[j][i]<<" ";}cout<<endl;}
}
// 输出为:ZhangSan LiSi WangWu
// ZhaoLiu SunQi ShenBa
// WuJiu 0 0
我们也可以用更熟悉的方式来实现赋值:
int main()
{Array<Array<string>> vs;vs[0][0]="ZhangSan";vs[0][1]="LiSi";vs[0][2]="WangWu";vs[1][0]="ZhaoLiu";vs[1][1]="SunQi";vs[1][2]="ShenBa";vs[2][0]="WuJiu";for (int j=0;j<3;j++){for(int i=0;i<3;i++){cout<<vs[j][i]<<" ";}cout<<endl;}
}
// 输出为:ZhangSan LiSi WangWu
// ZhaoLiu SunQi ShenBa
// WuJiu
大家看懂了吗?
本节我们讲了类模板的简单用法,理论并不复杂,但想要掌握还要多加练习。