第 14章 模板模板 ( Template)是 C++ 语言代码重用和多态性的一个集中表现。模板是提供这样一个转换机制:由程序员定义一种操作或一个类,而该操作或类却可以适应几乎所有的数据类型。在一定意义上,模板类似宏定义或函数重载,但它书定更为简洁,
使用更加灵活,适应性更强。
模板分函数模板和类模板。前者为程序员编写通用函数提供了一种手段;而后者则为程序员设计通用类奠定了基础。
14.1 函数模板
14.1.1 定义模板函数利用函数模板定义的函数叫做模板函数,定义模板函数应具有以下的一般形式:
template<class T>
type Function_Name(arg_list)
{
// Function_Body;
}
其中:关键字 template 指出下面将要说明的是一个模板;而
<class T> 则是该函数所要求的参数,这里的 class 与类无关,
而是与参数名 T 一起指出:“这是一个用户定义的数据类型”,它与一对尖括号是模板语法的组成部分。
例:定义模板函数
// MAXMIN.H
template<class T>
T Max(T a,T b)
{
return (a > b)? a,b;
}
template<class T>
T Min(T a,T b)
{
return (a < b)? a,b;
}
定义模板函数时应注意:
1,在模板的参数表中,至少得有一个参数的数据类型为模板的参数类型 T;模板函数的返回值的数据类型也可以为 T。
2,模板可以带有多个不同数据类型的参数,比如:
template<class T1,class T2,class T3>
int f(T1 arg1,T2 arg2,T3 arg3)
{
//…
}
3,模板参数的数据类型也可以是已存在的数据类型,比如:
template<class T,int n>
T f(T arg)
{
int i = n;
//…
}
4,函数可以带有模板中未给出的、已存在的数据类型的参数,
比如:
template<class T>
T f(T arg,int n)
{
//…
}
5,模板函数的函数体中必须使用模板参数。
14.1.2 使用模板函数调用模板函数与调用一般函数的方法无二致。
例:使用 Max()和 Min()模板函数
#include <iostream.h>
#include "maxmin.h"
void main()
{
int i = 3,j = 5;
float f1 = 12.34,f2 = 23.45;
cout << Max(i,j) << endl;
cout << Min(f1,f2) << endl;
cout << Max('a','b')
}
程序的输出为:
5
12.34
b
14.2 类模板
14.2.1 定义模板类利用类模板定义的类叫做模板类。定义模板类具有如下的一般形式:
template<class T>
class Class_Name {
// Members;
};
例:定义模板类
// ANYTYPE.H
template<class T>
class AnyType {
private:
T x,y;
public:
AnyType(T a,T b),x(a),y(b) {}
void SetX(T a) { x = a; }
void SetY(T b) { y = b; }
T GetX() { return x; }
T GetY() { return y; }
};
// End of ANYTYPE.H
上例中,模板类 AnyType 中所有成员函数均在类中定义成内联函数。实际上,与一般类相同,模板类中的任一成员函数均可以在类外定义。在类外定义成员函数的一般形式与定义模板函数基本相同:
template<class T>
type Class_Name<T>,,Func_Name(ages)
{
// Function_Body;
}
请注意这里类名的写法:模板类与普通类的不同之处在于模板类的类名一定具有 Class_Name<T> 的形式。
例:定义一个栈模板类
// ANYSTACK.H
#if !defined _ANYSTACK_H_
#define _ANYSTACK_H_
template<class T,int n = 10>
class AnyStack {
private:
T Stack[n];
int TopMost;
int Top;
public:
AnyStack(),TopMost(n),Top(0) {}
int Push(T);
int Pop(T&);
}
template<class T,int n>
int AnyStack<T,n>,,Push(T Elem)
{
if(Top == TopMost) // 栈已满
return 0;
Stack[Top ++] = Elem;
return 1;
}
template<class T,int n>
int AnyStack<T,n>,,Pop(T& rElem)
{
if(Top == 0) // 栈为空栈
return 0;
rElem = Stack[-- Top];
return 1;
}
#dneif
14.2.2 使用模板类与一般类一样,在使用一个类时必须先行说明属于该类类型的对象(或指针)。然而,模板类对象的说明形式与一般类对象有所不同,说明模板类对象具有如下的一般形式:
Class_Name<type> Obj_Name;
其中,type为任一已存在的数据类型(包括类类型)。
例:使用 AnyStack 模板类
#inclucd <iostream.h>
#include "anystack.h"
void main()
{
AnyStack<int> iStk;
AnyStack<float 5> fStk;
int i,iE;
float fE;
for(i = 1; i < 6; i ++) {
iStk.Push(i);
fStk.Push(i * 3.14);
}
while(iStk.Pop(iE))
cout << iE << '\t';
cout << endl;
while(fStk.Pop(fE))
cout << fE << '\t';
cout << endl;
}
该程序的输出为:
1 2 3 4 5
3.14 6.28 9.42 12.56 15.70
^^H
Prev NextInfo
综合举例问题 设计一个双链表类,并为其提供相应的公有接口。
分析 所谓双链表是指一种线性表,该表由一个个叫做结点
( Node)的元素组成,各结点分别有两个指针:一个指向其前趋( Previous);一个指向其后继( Next)。 如下图所示。
从上图可以看出:双链表的设计可以分为两部分 —— 结点的设计和链表的设计。为了便于链表对结点的访问,可以将链表类设计成结点类的友元。
结点的运算(操作)主要包括:创建结点、销毁结点、访问结点的信息( Info)域、访问结点的链域( Prev 或 Next)。
双链表的运算主要包括:创建一个空链表、销毁整个链表、在链表中插入一个结点、删除链表中的一个指定结点、在链表中查找一个指定内容的结点。其中插入操作又可以分为:在链表尾部插入(也叫追加)、在某一指定结点之前插入(称为前插入)、在某一指定结点之后插入(称为后插入)。同样,删除操作也可以分为:自身删除、前删除和后删除。
// OBJECT.H
#if !defined _OBJECT_H_
#define _OBJECT_H_
class Object {
public:
Object() {}
virtual ~Object() {}
virtual int operator ==(Object&) = 0;
virtual int operator >(Object&) = 0;
virtual int operator <(Object&) = 0;
virtual void Show() = 0;
};
#endif
// NODE.H
#include "object.h"
class NODE {
private:
Object * Info;
NODE *Prev,*Next;
public:
NODE(),Info(0),Prev(0),Next(0) {}
~NODE() { delete Info; }
void SetInfo(Object& rObj) { Info = rObj; }
Object* GetInfo() { return Info; }
void ShowInfo() { Info.Show(); }
friend class DLIST;
};
// DLIST.H
#if !defined _DLIST_H_
#define _DLIST_H_
#include "node.h"
class DLIST {
private:
NODE *Head,*Tail;
void DestroyList();
public:
DLIST(),Head(0),Tail(0) {}
void AppendNode(NODE*);
void InsertPrev(NODE*,NODE*);
void InsertNext(NODE*,NODE*);
NODE* DeleteNode(NODE*);
NODE* DeletePrev(NODE*);
NODE* DeleteNext(NODE*);
NODE* Lookup(Object&);
void ShowList();
void DestroyList();
~DLIST() { DestroyList(); }
};
#endif
// DLIST.CPP
#include "dlist.h"
void DLIST,,AppendNode(NODE* node)
{
if(Head == 0)
Head = Tail = node;
else {
Tail->Next = node;
node->Prev = Tail;
Tail = node;
}
} H
T
n
void DLIST,,InsertPrev(NODE* node,NODE* pPos)
{
node->Prev = pPos->Prev;
node->Next = pPos;
pPos->Prev = node;
node->Prev->Next = node;
}
void DLIST,,InsertNext(NODE* node,NODE* pPos)
{
作为习题请同学自己编写 ;
}
NODE* DLIST,,DeleteNode(NODE* node)
{
node->Prev->Next = node->Next;
node->Next->Prev = node->Prev;
node->Prev = node->Next = 0;
return node;
}
NODE* DLIST,,DeletePrev(NODE* pPos)
{
return DeleteNode(pPos->Prev);
}
NODE* DLIST,,DeleteNext(NODE* pPos)
{
return DeleteNode(pPos->Next);
}
void DLIST,,ShowList()
{
NODE* pnode = Head;
while(pnode) {
pnode->ShowInfo();
pnode = pnode->Next;
}
}
NODE* DLIST,,Lookup(Object& rObj)
{
NODE* pnode = Head;
while(pnode) {
if(*(pnode->Info) == rObj) // 调用重载了的关系运算符
return pnode;
pnode = pnode->Next;
}
return 0;
}
void DLIST,,DestroyList()
{
NODE *pnode = Head;
while(pnode) {
Head = pnode->Next;
delete pnode->Info;
delete pnode;
pnode = Head;
}
}
// End of DLIST.CPP
// COMPLEX.H
#if !defined _COMPLEX_H_
#define _COMPLEX_H_
#include "object.h"
class Complex,public Object {
private:
float Real;
float Img;
public:
Complex(),Real(0),Img(0) {}
Complex(float r,float i),Real(r),Img(i) {}
~Complex() {}
void SetReal(float r) { Real = r; }
void SetImg(float i) { Img = i; }
float GetReal() { return Real; }
float GetImg() { return Img; }
virtual int operator ==(Object&);
virtual int operator >(Object&);
virtual int operator <(Object&);
virtual void Show();
};
#endif
// End of COMPLEX.H
// COMPLEX.CPP
#include <iostream.h>
#include "complex.h"
int Complex,,operator ==(Object& rObj)
{
Complex &com = (Complex&)rObj;
return (com.Real == Real && com.Img == Img);
}
int Complex,,operator >(Object& rObj) // 自编
int Complex,,operator <(Object& rObj) // 自编
void Complex,,Show()
{
cout << Real << " + " << Img << 'i' << endl;
}
// End of COMPLEX.CPP
// TESTLIST.CPP
#include <iostream.h>
#include "dlist.h"
#include "complex.h"
void main()
{
DLIST List;
NODE *pn,node;
Complex *pc,Key;
for(int i = 1; i <= 5; i ++) {
pc = new Complex(i * 3.5,i * 4.6);
pn = new NODE;
pn->SetInfo(pc);
List.AppendNode(pn);
}
List.ShowList();
cout << endl;
Key.SetReal(10.5);
Key.SetImg(13.8);
pn = List.Lookup(Key);
if(pn)
pn = List.DeleteNode(pn);
List.ShowList();
delete pn;
}
该程序的输出为:
3.5 + 4.6i
7 + 9.2i
10.5 + 13.8i
14 + 18.4i
17.5 + 23i
3.5 + 4.6i
7 + 9.2i
14 + 18.4i
17.5 + 23i
使用更加灵活,适应性更强。
模板分函数模板和类模板。前者为程序员编写通用函数提供了一种手段;而后者则为程序员设计通用类奠定了基础。
14.1 函数模板
14.1.1 定义模板函数利用函数模板定义的函数叫做模板函数,定义模板函数应具有以下的一般形式:
template<class T>
type Function_Name(arg_list)
{
// Function_Body;
}
其中:关键字 template 指出下面将要说明的是一个模板;而
<class T> 则是该函数所要求的参数,这里的 class 与类无关,
而是与参数名 T 一起指出:“这是一个用户定义的数据类型”,它与一对尖括号是模板语法的组成部分。
例:定义模板函数
// MAXMIN.H
template<class T>
T Max(T a,T b)
{
return (a > b)? a,b;
}
template<class T>
T Min(T a,T b)
{
return (a < b)? a,b;
}
定义模板函数时应注意:
1,在模板的参数表中,至少得有一个参数的数据类型为模板的参数类型 T;模板函数的返回值的数据类型也可以为 T。
2,模板可以带有多个不同数据类型的参数,比如:
template<class T1,class T2,class T3>
int f(T1 arg1,T2 arg2,T3 arg3)
{
//…
}
3,模板参数的数据类型也可以是已存在的数据类型,比如:
template<class T,int n>
T f(T arg)
{
int i = n;
//…
}
4,函数可以带有模板中未给出的、已存在的数据类型的参数,
比如:
template<class T>
T f(T arg,int n)
{
//…
}
5,模板函数的函数体中必须使用模板参数。
14.1.2 使用模板函数调用模板函数与调用一般函数的方法无二致。
例:使用 Max()和 Min()模板函数
#include <iostream.h>
#include "maxmin.h"
void main()
{
int i = 3,j = 5;
float f1 = 12.34,f2 = 23.45;
cout << Max(i,j) << endl;
cout << Min(f1,f2) << endl;
cout << Max('a','b')
}
程序的输出为:
5
12.34
b
14.2 类模板
14.2.1 定义模板类利用类模板定义的类叫做模板类。定义模板类具有如下的一般形式:
template<class T>
class Class_Name {
// Members;
};
例:定义模板类
// ANYTYPE.H
template<class T>
class AnyType {
private:
T x,y;
public:
AnyType(T a,T b),x(a),y(b) {}
void SetX(T a) { x = a; }
void SetY(T b) { y = b; }
T GetX() { return x; }
T GetY() { return y; }
};
// End of ANYTYPE.H
上例中,模板类 AnyType 中所有成员函数均在类中定义成内联函数。实际上,与一般类相同,模板类中的任一成员函数均可以在类外定义。在类外定义成员函数的一般形式与定义模板函数基本相同:
template<class T>
type Class_Name<T>,,Func_Name(ages)
{
// Function_Body;
}
请注意这里类名的写法:模板类与普通类的不同之处在于模板类的类名一定具有 Class_Name<T> 的形式。
例:定义一个栈模板类
// ANYSTACK.H
#if !defined _ANYSTACK_H_
#define _ANYSTACK_H_
template<class T,int n = 10>
class AnyStack {
private:
T Stack[n];
int TopMost;
int Top;
public:
AnyStack(),TopMost(n),Top(0) {}
int Push(T);
int Pop(T&);
}
template<class T,int n>
int AnyStack<T,n>,,Push(T Elem)
{
if(Top == TopMost) // 栈已满
return 0;
Stack[Top ++] = Elem;
return 1;
}
template<class T,int n>
int AnyStack<T,n>,,Pop(T& rElem)
{
if(Top == 0) // 栈为空栈
return 0;
rElem = Stack[-- Top];
return 1;
}
#dneif
14.2.2 使用模板类与一般类一样,在使用一个类时必须先行说明属于该类类型的对象(或指针)。然而,模板类对象的说明形式与一般类对象有所不同,说明模板类对象具有如下的一般形式:
Class_Name<type> Obj_Name;
其中,type为任一已存在的数据类型(包括类类型)。
例:使用 AnyStack 模板类
#inclucd <iostream.h>
#include "anystack.h"
void main()
{
AnyStack<int> iStk;
AnyStack<float 5> fStk;
int i,iE;
float fE;
for(i = 1; i < 6; i ++) {
iStk.Push(i);
fStk.Push(i * 3.14);
}
while(iStk.Pop(iE))
cout << iE << '\t';
cout << endl;
while(fStk.Pop(fE))
cout << fE << '\t';
cout << endl;
}
该程序的输出为:
1 2 3 4 5
3.14 6.28 9.42 12.56 15.70
^^H
Prev NextInfo
综合举例问题 设计一个双链表类,并为其提供相应的公有接口。
分析 所谓双链表是指一种线性表,该表由一个个叫做结点
( Node)的元素组成,各结点分别有两个指针:一个指向其前趋( Previous);一个指向其后继( Next)。 如下图所示。
从上图可以看出:双链表的设计可以分为两部分 —— 结点的设计和链表的设计。为了便于链表对结点的访问,可以将链表类设计成结点类的友元。
结点的运算(操作)主要包括:创建结点、销毁结点、访问结点的信息( Info)域、访问结点的链域( Prev 或 Next)。
双链表的运算主要包括:创建一个空链表、销毁整个链表、在链表中插入一个结点、删除链表中的一个指定结点、在链表中查找一个指定内容的结点。其中插入操作又可以分为:在链表尾部插入(也叫追加)、在某一指定结点之前插入(称为前插入)、在某一指定结点之后插入(称为后插入)。同样,删除操作也可以分为:自身删除、前删除和后删除。
// OBJECT.H
#if !defined _OBJECT_H_
#define _OBJECT_H_
class Object {
public:
Object() {}
virtual ~Object() {}
virtual int operator ==(Object&) = 0;
virtual int operator >(Object&) = 0;
virtual int operator <(Object&) = 0;
virtual void Show() = 0;
};
#endif
// NODE.H
#include "object.h"
class NODE {
private:
Object * Info;
NODE *Prev,*Next;
public:
NODE(),Info(0),Prev(0),Next(0) {}
~NODE() { delete Info; }
void SetInfo(Object& rObj) { Info = rObj; }
Object* GetInfo() { return Info; }
void ShowInfo() { Info.Show(); }
friend class DLIST;
};
// DLIST.H
#if !defined _DLIST_H_
#define _DLIST_H_
#include "node.h"
class DLIST {
private:
NODE *Head,*Tail;
void DestroyList();
public:
DLIST(),Head(0),Tail(0) {}
void AppendNode(NODE*);
void InsertPrev(NODE*,NODE*);
void InsertNext(NODE*,NODE*);
NODE* DeleteNode(NODE*);
NODE* DeletePrev(NODE*);
NODE* DeleteNext(NODE*);
NODE* Lookup(Object&);
void ShowList();
void DestroyList();
~DLIST() { DestroyList(); }
};
#endif
// DLIST.CPP
#include "dlist.h"
void DLIST,,AppendNode(NODE* node)
{
if(Head == 0)
Head = Tail = node;
else {
Tail->Next = node;
node->Prev = Tail;
Tail = node;
}
} H
T
n
void DLIST,,InsertPrev(NODE* node,NODE* pPos)
{
node->Prev = pPos->Prev;
node->Next = pPos;
pPos->Prev = node;
node->Prev->Next = node;
}
void DLIST,,InsertNext(NODE* node,NODE* pPos)
{
作为习题请同学自己编写 ;
}
NODE* DLIST,,DeleteNode(NODE* node)
{
node->Prev->Next = node->Next;
node->Next->Prev = node->Prev;
node->Prev = node->Next = 0;
return node;
}
NODE* DLIST,,DeletePrev(NODE* pPos)
{
return DeleteNode(pPos->Prev);
}
NODE* DLIST,,DeleteNext(NODE* pPos)
{
return DeleteNode(pPos->Next);
}
void DLIST,,ShowList()
{
NODE* pnode = Head;
while(pnode) {
pnode->ShowInfo();
pnode = pnode->Next;
}
}
NODE* DLIST,,Lookup(Object& rObj)
{
NODE* pnode = Head;
while(pnode) {
if(*(pnode->Info) == rObj) // 调用重载了的关系运算符
return pnode;
pnode = pnode->Next;
}
return 0;
}
void DLIST,,DestroyList()
{
NODE *pnode = Head;
while(pnode) {
Head = pnode->Next;
delete pnode->Info;
delete pnode;
pnode = Head;
}
}
// End of DLIST.CPP
// COMPLEX.H
#if !defined _COMPLEX_H_
#define _COMPLEX_H_
#include "object.h"
class Complex,public Object {
private:
float Real;
float Img;
public:
Complex(),Real(0),Img(0) {}
Complex(float r,float i),Real(r),Img(i) {}
~Complex() {}
void SetReal(float r) { Real = r; }
void SetImg(float i) { Img = i; }
float GetReal() { return Real; }
float GetImg() { return Img; }
virtual int operator ==(Object&);
virtual int operator >(Object&);
virtual int operator <(Object&);
virtual void Show();
};
#endif
// End of COMPLEX.H
// COMPLEX.CPP
#include <iostream.h>
#include "complex.h"
int Complex,,operator ==(Object& rObj)
{
Complex &com = (Complex&)rObj;
return (com.Real == Real && com.Img == Img);
}
int Complex,,operator >(Object& rObj) // 自编
int Complex,,operator <(Object& rObj) // 自编
void Complex,,Show()
{
cout << Real << " + " << Img << 'i' << endl;
}
// End of COMPLEX.CPP
// TESTLIST.CPP
#include <iostream.h>
#include "dlist.h"
#include "complex.h"
void main()
{
DLIST List;
NODE *pn,node;
Complex *pc,Key;
for(int i = 1; i <= 5; i ++) {
pc = new Complex(i * 3.5,i * 4.6);
pn = new NODE;
pn->SetInfo(pc);
List.AppendNode(pn);
}
List.ShowList();
cout << endl;
Key.SetReal(10.5);
Key.SetImg(13.8);
pn = List.Lookup(Key);
if(pn)
pn = List.DeleteNode(pn);
List.ShowList();
delete pn;
}
该程序的输出为:
3.5 + 4.6i
7 + 9.2i
10.5 + 13.8i
14 + 18.4i
17.5 + 23i
3.5 + 4.6i
7 + 9.2i
14 + 18.4i
17.5 + 23i