“模板”学习笔记
ps:http://www.cnblogs.com/uniqueliu/archive/2011/08/09/2127627.html
====================C++中引出模板的原因====================
我们大家都知道,C++给我们提供了众多的数据类型,比如说整型、浮点型、双精度型、布尔型等等。而且C++还为我们提供了一种函数重载的方式,即函数名相同,但是类型不同,那么也可以被C++编译器所认识到,进而可以输出不同的结果。下面,我们以一个程序为例来说明:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
#include <iostream>using namespace std;int sum(int a,int b);//两个整型变量相加double sum(double a,double b);//两个双精度型变量相加float sum(float a,float b);///两个浮点型型变量相加int sum(int a,int b){ return a+b;}double sum(double a,double b){ return a+b;}float sum(float a,float b){ return a+b;}int main(){ int x=1,y=2; cout<<"整型变量的值为:"<<x<<" "<<y<<endl; cout<<"相加的结果为:"<<sum(x,y)<<endl; double l=1.11111f,m=2.22222f; cout<<"双精度型变量的值为:"<<l<<" "<<m<<endl; cout<<"相加的结果为:"<<sum(l,m)<<endl; float p=1.2,q=2.3; cout<<"浮点型变量的值为:"<<p<<" "<<q<<endl; cout<<"相加的结果为:"<<sum(p,q)<<endl; return 0;} |
在上面这个程序的第4、5和6行,我们分别重载了sum()函数,这三个sum()函数的功能是完全一样的,都是返回一个两数之和。但是我们注意到,每个sum()函数的类型不一样,这样,在主程序中输入不同的参数时,编译器会自动调用与之类型匹配的sum()函数输出结果。程序运行的结果如下:

上面这个程序虽然实现重载sum()函数达到对每种不同类型的参数的响应,但是,程序十分臃肿,一个小小的相加程序居然用了35行!这是不被接受的。那么我们不禁要想一想了,有没有什么办法能够替代这种冗长的程序呢?
答案其实就是运用模板!有了模板,我们就不需要重载了,接下来我们同样以这个相加的程序来作为例子说明下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#include <iostream>using namespace std;template<class ElementType>//声明模板类型ElementType sum(ElementType a,ElementType b){ return a+b;}int main(){ int x=1,y=2; cout<<"整型变量的值为:"<<x<<" "<<y<<endl; cout<<"相加的结果为:"<<sum(x,y)<<endl; double l=1.11111f,m=2.22222f; cout<<"双精度型变量的值为:"<<l<<" "<<m<<endl; cout<<"相加的结果为:"<<sum(l,m)<<endl; float p=1.2,q=2.3; cout<<"浮点型变量的值为:"<<p<<" "<<q<<endl; cout<<"相加的结果为:"<<sum(p,q)<<endl; return 0;} |
呵呵,直观上这个程序就比刚刚那个简洁,只有22行。下面我们来认真分析一下这个程序到底说了什么。首先我们可以看到,在程序的第3行,我们就声明了一个模板类型ElementType。这就是模板的声明格式,如下:
template<class Type1,class Type2> |
template是声明模板的关键字,尖括号中的class表示后面跟着的参数为自定义的模板类型,上面我们定义的类型为Type1和Type2。应该特别注意的是,模板声明之后不加;号!!!
之后在程序的5到8行我们用自定义的类型ElementType创建了一个模板函数。函数模板说白了就是一个模子,用这个模子我们就可以创建出许多功能相同、参数类型和返回值不同的函数,从而这样就实现了代码的重用~~~在这个函数中,我们并没有为sum()函数提供具体的参数类型,只用ElementType来代替。这样编译器就会根据实际传递进来的类型和我们自定义的函数模板来创建sum()函数。由于这种工作分别在程序的第14行、17行和20行被执行了3次,所以说sum()函数被重载了3次,以适应不同的类型的数据。
=================如何对模板进行重载====================
我们在运用一个函数模板的时候,很可能会增加这个函数的功能。比如说原先我们有一个交换函数:
void Swap(ElementType,ElementType); |
它的功能就是对两个数进行交换。但是现在如果我们想对两个数组中的每一个数进行交换,那么就需要重载这个Swap函数,并且给它添加一个新的变量:int n。这个函数的作用就是循环数组中的每个元素,那么这个重载的Swap()函数就应该用如下方式进行声明:
void Swap(ElementType a[],ElementType b[],int n); |
这样一来,women就对原有的Swap()函数进行了重载,即功能上的升级。下面是这个程序的例子:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
#include <iostream>using namespace std;const int num=5;//Swap函数的第一个版本template<class ElementType>void Swap(ElementType a,ElementType b){ ElementType temp; cout<<"交换前,元素a和元素b的值为:"<<endl; cout<<"a="<<a<<"\tb="<<b<<endl;a cout<<"调用Swap(ElementType,ElementType)函数:"<<endl; temp=b; b=a; a=temp; cout<<"a="<<a<<"\tb="<<b<<endl;}//Swap函数的第二个版本template<class ElementType>void Swap(ElementType a[],ElementType b[],int n){ ElementType temp; cout<<"交换前,数组a[]和数组b[]的值为:"<<endl; for(int i=0;i<n;i++) { cout<<"a["<<i<<"]为:"<<a[i]<<" "; } cout<<endl; for(int i=0;i<n;i++) { cout<<"b["<<i<<"]为:"<<b[i]<<" "; } cout<<endl; cout<<"调用Swap(ElementType,ElementType,int)函数:"<<endl; for(int i=0;i<n;i++) { temp=b[i]; b[i]=a[i]; a[i]=temp; } for(int i=0;i<n;i++) { cout<<"a["<<i<<"]为:"<<a[i]<<" "; } cout<<endl; for(int i=0;i<n;i++) { cout<<"b["<<i<<"]为:"<<b[i]<<" "; } cout<<endl;}int main(){ int x=1,y=2; Swap(x,y); int num1[num]={1,2,3,4,5}; int num2[num]={6,7,8,9,10}; Swap(num1,num2,num); return 0; } |
注意,在这个程序的第5行和18行我们都定义了一个模板类型ElementType。它用在紧接其后的模板函数的定义。这个程序主要完成额功能就是对两个数进行交换,同时对两个数组进行交换。下面就是这个程序的运行结果:

通过这个程序的运行结果,我们可以清楚的看到,利用模板重载这个概念,我们可以升级原有的函数,使之达到功能升级的地步~~
=================箴言:函数模板是无法进行重载的====================
函数模板是无法进行重载的,这个原因比较无语。不是C++规定的,而是我们根本就无法这样做,因为如果这样做了,编译器根本就认不到~~下面我们就举一个例子来说所这个问题:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
#include <iostream>using namespace std;struct people{ char name[10]; int age;};template<class ElementType>void Swap(ElementType &a,ElementType &b){ ElementType temp; temp=b; b=a; a=temp;}int main(){ int x=1,y=2; cout<<"交换之前,x="<<x<<"\ty="<<y<<endl; Swap(x,y); cout<<"交换之后,x="<<x<<"\ty="<<y<<endl; people person_1={"unqiue",22}; people person_2={"jack",30}; cout<<"交换之前,第一个人的名字叫:"<<person_1.name<<"\t年龄为:"<<person_1.age<<endl; cout<<"交换之前,第二个人的名字叫:"<<person_2.name<<"\t年龄为:"<<person_2.age<<endl; Swap(person_1,person_2); cout<<"调用Swap()函数的结果:"<<endl; cout<<"交换之后,第一个人的名字叫:"<<person_1.name<<"\t年龄为:"<<person_1.age<<endl; cout<<"交换之后,第二个人的名字叫:"<<person_2.name<<"\t年龄为:"<<person_2.age<<endl; return 0;} |
首先在这个程序的第3行我们定义了一个结构体变量people,它含有两个参数,名字(name)和年龄(age)。然后我们在程序的第9行到15行写了一个模板函数Swap(),它的作用就是交换a和b中的值。在主程序中,我们首先在20行调用Swap()函数来交换整型变量x和y的值;之后我们在程序的第22和23行分别用结构体people定义了两个人person_1和person_2的名字和年龄,然后我们想在程序的第26行交换他们。输出如下:

可以看到,在输出中我们调用Swap()函数之后,第一个人和第二个人其实只是对换了一下位置。即person_1里面存储的就是jack和30,而person_2里面存储的就是unique和22。反正我觉着这种程序在实际生活中没有任何意思,因为不就是调换一下位置么。真正有难度的是如何让jack的年龄和unqiue的年龄进行对调!下面我们就来看看这种程序:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
#include <iostream>using namespace std;struct people{ char name[10]; int age;};template<class ElementType>void Swap(ElementType &a,ElementType &b){ ElementType temp; temp=b; b=a; a=temp;}template<class ElementType>void Swap_1(ElementType &a,ElementType &b){ int temp;//用于存放年龄 temp=b.age; b.age=a.age; a.age=temp;}int main(){ int x=1,y=2; cout<<"交换之前,x="<<x<<"\ty="<<y<<endl; Swap(x,y); cout<<"交换之后,x="<<x<<"\ty="<<y<<endl; people person_1={"unqiue",22}; people person_2={"jack",30}; cout<<"交换之前,第一个人的名字叫:"<<person_1.name<<"\t年龄为:"<<person_1.age<<endl; cout<<"交换之前,第二个人的名字叫:"<<person_2.name<<"\t年龄为:"<<person_2.age<<endl; Swap(person_1,person_2); cout<<"调用Swap()函数的结果:"<<endl; cout<<"交换之后,第一个人的名字叫:"<<person_1.name<<"\t年龄为:"<<person_1.age<<endl; cout<<"交换之后,第二个人的名字叫:"<<person_2.name<<"\t年龄为:"<<person_2.age<<endl; Swap_1(person_1,person_2); cout<<"调用Swap_1()函数的结果:"<<endl; cout<<"交换之后,第一个人的名字叫:"<<person_1.name<<"\t年龄为:"<<person_1.age<<endl; cout<<"交换之后,第二个人的名字叫:"<<person_2.name<<"\t年龄为:"<<person_2.age<<endl; return 0;} |
在这个程序中,我们只想交换jack和unique的年龄,但是这是不可能通过重载Swap()函数实现。因为这里的Swap()函数并不涉及的到功能的升级啊之类的。所以它的返回值和参数的类型和在第9行定义的Swap()函数是一模一样的,这样一来,编译器就不能分辨出到底哪个Swap()函数才是我们想要的。所以在程序的第18行,我们只能用另外一个函数Swap_1()来代替原先那个Swap()函数,虽然它们的功能基本上相同~~
程序的输出结果如下:

上面这种解决办法我想是一件极其郁闷的事情!如果我们都像程序的第18行到24行这样来运用模板,那模板的作用就根本没有发挥出来嘛。我们还不如把程序的17行到24行换成:
|
1
2
3
4
5
6
7
|
void Swap_1(people &a,people &b){ int temp;//用于存放年龄 temp=b.age; b.age=a.age; a.age=temp;} |
这样的输出结果同上,囧!这样一来,模板真正没有什么存在的价值了!!!OK~~通过这两个程序的对比,我们就可以看出来函数模板是无法重载的。那么我们有没有什么其他的办法来解决这个问题的呢?如何才能让重载函数模板呢?
=================具体化函数模板的方法====================
要是我们想要解决《“模板”学习笔记(3)-----为啥函数模板不能重载》讨论的函数模板无法重载的问题,我们必须要利用C++为模板提供的一个特殊的属性,一个具体化函数定义的特性,即显式具体化一个函数模板。比如说,我们将Swap()函数声明成:
void Swap(ElementType &a,ElementType &b) |
这就表示Swap()函数中有两个不具体的参数ElementType &a和ElementType &b,而我们现在要具体化这两个参数,比如说我们要把这两个参数换成people结构体,即把Swap()函数中的参数换成这样:
people &a,people &b |
这样一来,当我们在程序中想Swap()函数传递关于people结构体的参数时,编译器就会立即找到具体化接受两个people类型的Swap()函数,而不会使用原先定义的模板。这就叫显式具体化一个函数模板,说白了就是在原有函数模板的基础上新增一个接受固定参数的函数。在C++中我们是这样具体化一个函数模板的:
template<>void Swap<people>(people &a,people &b); |
这样一来,在编译的时候,如果我们为Swap()函数传递的参数为people类型的参数,那么编译器就会根据这个模板自动创建下面这个函数:
void Swap(people &a,people &b) |
这样一来,就达到了重载函数模板的问题了。但是有一点需要注意的是,我们在这里如果具体化了关于people的函数模板,那么具体化类型为people的函数模板只可适用于people结构的变量,其他变量还是照样会调用原先定义的Swap()模板函数的。
下面,我们来用一个例子演示一下这个问题:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
#include <iostream>using namespace std;struct people{ char name[10]; int age;};template<class ElementType>void Swap(ElementType &a,ElementType &b){ ElementType temp; temp=b; b=a; a=temp;}template<>void Swap<people>(people &a,people &b){ int temp; temp=b.age; b.age=a.age; a.age=temp;}int main(){ int x=1,y=2; cout<<"交换之前,x="<<x<<"\ty="<<y<<endl; Swap(x,y); cout<<"交换之后,x="<<x<<"\ty="<<y<<endl; people person_1={"unqiue",22}; people person_2={"jack",30}; cout<<"交换之前,第一个人的名字叫:"<<person_1.name<<"\t年龄为:"<<person_1.age<<endl; cout<<"交换之前,第二个人的名字叫:"<<person_2.name<<"\t年龄为:"<<person_2.age<<endl; Swap(person_1,person_2); cout<<"调用具体化为people的Swap()函数的结果:"<<endl; cout<<"交换之后,第一个人的名字叫:"<<person_1.name<<"\t年龄为:"<<person_1.age<<endl; cout<<"交换之后,第二个人的名字叫:"<<person_2.name<<"\t年龄为:"<<person_2.age<<endl; return 0;} |
程序的输出如下:

从输出我们就可以看出,当传递给编译器的Swap()函数的参数为整型变量的时候,它照样会调用位于程序第9行的Swap()函数模板,但是如果传递给Swap()函数的参数为people结构体类型的变量时,编译器却调用了位于程序第17行的具体化了的函数模板,从而只是交换两个人的年龄大小,而不是他们的名字。
=================编译器处理函数模板的方法:实例化模板函数====================
C++的编译器在遇到一个函数模板的时候,它首先要做的是确定该函数的参数,这就叫做把一个函数模板修改成一个具有具体参数的函数。这种方法一般而言是隐式转换的,我们一般是看不见的,但是C++也提供了一种显示转换的方法,如下:
template void Swap<int>(int &a,int &b); |
这就是实例化模板函数的声明,上面这个式子把Swap()函数实例化为一个接受整型变量的函数。它与具体化函数模板不同之处在于:
- 具体化函数模板告诉了编译器利用此模板生成一个具体化了参数类型的重载函数,这样我们就可以通过该函数的定义来修改这个这个重载的函数,从而达到功能升级的目的。比如说在《“模板”学习笔记(4)-----如何解决函数模板的重载问题》中,我们就在程序中重载了这个Swap()函数,使它能够在面对结构体people类型的变量时,只交换他们的年龄(age),而不交换他们的名字。
- 实例化模板函数就不是这样。编译器根据其他已有模板的定义部分创建参数类型为一个准确类型的参数,这里实例化模板函数的作用只在于根据传递进来的具体参数创建一个具体的重载函数,而不会改变其定义的功能。比如说,Swap()模板函数接受的参数为ElementType &a和ElementType &b,而我们要传递两个整型变量x和y的时候,编译器就会自动将Swap()函数声明成这样:
voidSwap(int&x,int&y);这就表示编译器根据最初的函数模板Swap()生成了一个关于该Swap()函数的实例,这个过程就叫实例化模板函数。
通过上面的比较我们就可以清楚的看出来,具体化函数模板其实是一个有针对性的构思,而实例化模板函数却是一个行为,一个真真切切的动作。编译器会根据我们定义的模板在最终生成一个实例化之后的模板函数,我们就把这个由函数模板生成的函数叫做模板实例。而模板的定义在编译前是不会生成这样实例化的函数的,它只用于生成该函数的一个解决方案,^_^,也就是说,它为编译器生成一个认为可行的思路。所以说:
- 显式实例化模板函数是在编译时根据某个非具体参数类型的函数模板创建了一个具体参数类型的完整函数,由于他只是具体化了函数的参数类型,并没有修改函数的功能,因此没有必要,也不能够重写函数的定义部分;
- 具体化函数模板则即具体化了函数的参数类型,又要修改函数的功能。
p.s. 如果我们实例化模板函数之后还修改了其定义,那么编译器就会报错,程序如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
#include <iostream>using namespace std;struct people{ char name[10]; int age;};template<class ElementType>void Swap(ElementType &a,ElementType &b){ ElementType temp; temp=b; b=a; a=temp;}//template<>void Swap<people>(people &a,people &b)void Swap<people>(people &a,people &b){ int temp; temp=b.age; b.age=a.age; a.age=temp;}int main(){ int x=1,y=2; cout<<"交换之前,x="<<x<<"\ty="<<y<<endl; Swap(x,y); cout<<"交换之后,x="<<x<<"\ty="<<y<<endl; people person_1={"unqiue",22}; people person_2={"jack",30}; cout<<"交换之前,第一个人的名字叫:"<<person_1.name<<"\t年龄为:"<<person_1.age<<endl; cout<<"交换之前,第二个人的名字叫:"<<person_2.name<<"\t年龄为:"<<person_2.age<<endl; Swap(person_1,person_2); cout<<"调用具体化为people的Swap()函数的结果:"<<endl; cout<<"交换之后,第一个人的名字叫:"<<person_1.name<<"\t年龄为:"<<person_1.age<<endl; cout<<"交换之后,第二个人的名字叫:"<<person_2.name<<"\t年龄为:"<<person_2.age<<endl; return 0;} |
生成的错误指向19行,错误内容就是““Swap”: 非法使用显式模板参数”。
函数都可以有模板,同样嘛,类当然不会放过这么好一个机会咯。类模板其实很简单,和函数模板一样,都是要首先声明一下模板类型为啥,然后再在类里面所有的东东都用这个模板来代替。我们以一个程序来说明一下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#include <iostream>using namespace std;template<class ElementType>class Sum{public: Sum(ElementType A,ElementType B):a(A),b(B){} ElementType sum(){return a+b;} ElementType getA(){return a;} ElementType getB(){return b;}private: ElementType a,b;};int main(){ Sum<int> test(3,4); cout<<"第一个加数为:"<<test.getA()<<"第二个加数为:"<<test.getB()<<"\t和为:"<<test.sum()<<endl; Sum<double> test_2(3.4,7.9); cout<<"第一个加数为:"<<test_2.getA()<<"第二个加数为:"<<test_2.getB()<<"\t和为:"<<test_2.sum()<<endl; return 0;} |
看到没,很简单吧。首先我们在程序的第3行声明了一个模板ElementType,其次在类Sum的定义里面,我们针对所有的元素都采用了ElementType这个模板类型。和函数模板唯一不同之处在于程序第17行和第19行,在这里,我们需要在类名Sum后面的尖括号<>里面加上实际的类型int和double。这样就可以对不同的类型数据作出不同的响应。程序的输出结果如下:

OK~~这个就是类模板的简单示例咯
我们可以定义一个数组模板,并且利用该模板声明其数组成员。声明的方式非常简单,主需要一下两步:
|
1
2
|
template<class ElementType,int n>;ElementType a[n]; |
第一句话就是定义这个模板,注意其中的参数不再是1个了,而是2个。其中第一个参数就是一个模板的类型ElementType,表示数组a的类型;另外一个是整型变量n,众所周知,它代表的是该数组a的长度。下面,我用一个比较难的程序来说明一下数组模板的作用。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
#include <iostream>using namespace std;template<class ElementType,int n>class ArrayTemplate{public: ArrayTemplate(); ArrayTemplate(const ElementType &t); ElementType& operator[](int i); void show();private: ElementType a[n];};template<class ElementType,int n>ArrayTemplate<ElementType,n>::ArrayTemplate(){ cout<<"执行不带参数的构造函数\n"; for (int i=0;i<n;i++) { a[i]=(i+1); }}template<class ElementType,int n>ArrayTemplate<ElementType,n>::ArrayTemplate(const ElementType &t){ cout<<"执行带一个参数的构造函数\n"; for (int i=0;i<n;i++) { a[i]=t; }}template<class ElementType,int n>ElementType& ArrayTemplate<ElementType,n>::operator[](int i){ cout<<"执行下标运算符函数operator[]\n"; if (i<0||i>=n) { cout<<"超出数组的限制,程序终止!\n"; exit(EXIT_FAILURE); } return a[i];}template<class ElementType,int n>void ArrayTemplate<ElementType,n>::show(){ for(int i=0;i<n;i++) { cout<<"a["<<i<<"]="<<a[i]<<"\t"; } cout<<endl;}int main(){ ArrayTemplate<int,4>array_1; array_1.show(); ArrayTemplate<int,4>*array_2=new ArrayTemplate<int,4>[4]; for(int i=0;i<9;i++) { array_2[i]=array_1[i]; array_2[i].show(); } return 0;} |
程序分析:
首先我们在程序的第3行定义了一个数组模板,其类型为ElementType,数组长度为n。然后我们在类ArrayTemplate中于第7行声明了一个不带参数的构造函数,定义在第15行;再在第8行重新声明了一个带一个参数的构造函数,定义在第行。在程序的第9行我们重载了[]运算符。这里注意一下,我们在模板类的外面定义类中的函数的时候,应该有三点注意事项:
- 必须在函数前面重新声明一下模板,如程序的第14行所示;
- 要想调用模板类中的函数,不能直接写上类名::函数(),而是要在模板类之后加上模板定义,如15行所示,这样一来,编译器就会知道函数()是来自模板函数的了;
- 注意程序第33行的重载运算符operator函数的定义。我们在一开始应该先加上“模板类型&”,其次和定义其他模板函数类似,千万不能漏掉“模板类型&”。
下面,我们从主函数开始进行分析...
首先在第54行我们调用模板类创建了一个array_1的类型。它其中的变量为int型,然后数组元素一共有4个。这样一来,就表示在模板类ArrayTemplate中的的私有成员变量
ElementType a[n]; |
实际上就成为了以下的形式:
int a[4]; |
这样一来,在程序第55行调用show()函数的时候,for循环的次数就会限定为4次(因为n=4),这样就会输出由模板类对象array_1创建的4个数组a[0]、a[1]、a[2]、a[3]的值。然后我们再来看看程序的第56行,这里在堆中创建了4个对象,并把这4个对象放在一个数组中,这个数组就是array_2。由于这个数组中间存放的全部都是对象,那么我们就把array_2叫做对象数组。后面的for循环中我们首先来看看程序第59行的语句:
array_2[i]=array_1[i]; |
当i=0的时候这句话就相当于
array_2[0]=array_1[0]; |
注意右边的array_1[0],因为array_1是一个对象而不是一个数组,那么编译器就会调用我们自己在程序的第33行定义的重载[]运算符的函数。它返回的就是array_1中的数组a[i]的值。在这里,因为i=0,所以返回值就是a[0]。而我们从上面可以看到,a[0]=1所以相当于返回1。而特别注意的是array_2[0]的类型为ArrayTemplate类型,而a[0]的类型却是整型,编译器则会使用隐式类型转换:
array_2[0]=ArrayTemplate(a[0]); |
将a[0]强制转换为ArrayTemplate类型。这样就会调用位于程序第24定义的带一个参数的构造函数。在这个构造函数中,我们将a[0]的值作为t值传递进去,然后再for循环中将a[0]到a[3]的值全部赋值为t(即a[0],也就是1)。值得注意的一点就在于,这里t代表的a[0]是array_1的数组,而4次循环中的a[i]代表的是array_2对象数组中array_2[0]中的a[0]到a[3]的值!!所以会输出1,1,1,1也就不足为奇了。同理,后面的输出分析方法同前面一样。只有一点,当主程序的for循环,循环到i=4的时候。这时,就会执行operator[]函数中的if语句之后的内容,退出程序。
整个程序的输出证明了我们的理论:

OK,这个比较难的程序搞定了,也就标志了这个方面的模板应用学习完了,^_^,继续努力!!
具有多个参数的模板
一个模板中可以具有多个参数,即可以在一个模板中声明多个自定义的类型,如下面这句话:
template<class T1,class T2> |
而我们就可以利用这两个参数声明一个具有2种类型的成员。下面我用一个程序说下演示一下这个问题:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#include <iostream>#include <string>using namespace std;template<class T1,class T2>class show{public: void show1(T1 &a){cout<<"show1:"<<a<<endl;} void show2(T2 &a){cout<<"show2:"<<a<<endl;}};int main(){ show<int,string> a; int temp1=5; string temp2="Hello,C++!"; a.show1(temp1); a.show2(temp2); return 0;} |
在上面的程序中,我在主函数中将两个类型T1和T2分别设置成了int型和string类型。这样一来,我们在程序的第15行和16行定义的整型变量和string型变量就可以在17行和18行被输出,结果如下:

另外一个需要注意的问题,我们也可以为模板参数提供默认的类型,比如说:
template<class T1,class T2=string> |
这样一来,我们就把T2参数默认设置成了string类型。那么在上面主程序中,我们把14行换成:
show<int> a; |
这样还是相当于:
show<int ,string> a; |
整个程序示例如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#include <iostream>#include <string>using namespace std;template<class T1,class T2=string>class show{public: void show1(T1 &a){cout<<"show1:"<<a<<endl;} void show2(T2 &a){cout<<"show2:"<<a<<endl;}};int main(){ show<int> a; int temp1=5; string temp2="Hello,C++!"; a.show1(temp1); a.show2(temp2); return 0;} |
输出与上面一模一样,这里我就不把它粘上来了,^_^
浙公网安备 33010602011771号