1
第 4章 重载与模板
2
第 4章 重载与模板
函数名重载
函数模板
类模板
3
函数名重载
函数名重载与静态绑定
对象赋值与赋值运算符重载
类型转换与转换函数
插入/提取符及其重载
运算符重载规则
4
函数名重载与静态绑定
函数名重载
运算符重载
运算符重载的成员函数方式和友元函数方
式
5
函数名重载与静态绑定
绑定( binding)又称联编、聚束,是指程
序在编译或运行时将一个标识解释为一个
相应的程序实体
这里讲的绑定,主要指程序在编译或运行
时将一个函数名解释为一个相应的函数体
前面介绍的函数都是一个函数名对应一个
函数体。 C++也允许一个函数名对应多个
函数体,形成函数名的多态性。这种多态
性可以简单地概括为, 一个接口,多种方
法,
6
函数名重载
函数名重载就是多个函数使用一个函数名
先看下面的例子。
例 4.1.1 对任意两个数据变量或字符变量
进行交换
7
void swap(char & n1,char & n2) // 定义( 1)
{
char temp = n1;
n1 = n2;
n2 = temp;
}
void swap(int & n1,int & n2) // 定义( 2)
{
int temp = n1;
n1 = n2;
n2 = temp;
}
void swap(float & n1,float & n2) // 定义( 3)
{
float temp = n1;
n1 = n2;
n2 = temp;
}
void swap(double & n1,double & n2) // 定义( 4)
{
double temp = n1;
n1 = n2;
n2 = temp;
}
8
运算符重载
系统只预定义了少数几个用于基本类型的
运算符函数。对于更多的类型,需要程序
员自己去定义相应的运算符重载函数。通
过运算符重载,使已有的运算符可以用于
各种对象
9
运算符重载
用户定义类型的重载运算符,要求能访问
运算对象的私有成员
为此只能用
成员函数
友元函数
两种形式定义运算符重载
10
表达式 成员函数方式 友元函数方式
一元运算表达式,obj @
@ obj
obj.operator@ (0 );
obj.operator@( )
operator@(obj,0)
operator @ ( obj )
二元运算表达式,obj1 @ obj2 obj1.operator@ ( obj2
);
operator @ ( obj1,obj2 )
11
复数类重载两个类运算符
# include <iostream.h>
class complex{
private,
float real,// 实部
imag; // 虚部
public,
complex(float r = 0,float i = 0)
{
real = r; imag = i;
}
void print()
{
cout << "c(" << real << ',' << imag << ')' << endl;
}
};
12
成员函数方式
( a)对单目减,
complex /*返回类型 */ complex /*类名 */,,operator- ( )
{
return complex(-real,-imag);
}
( b)对双目加,
complex /*返回类型 */ complex /*类名 */,,opelater+ ( complex &c )
{
float r = this -> real + c.real;
float i = this -> imag + c.imag;
return complex(r,i); // 返回一个新对象,这时将调用构造函数
}
13
友元函数方式
( a)友元函数方式的双目 +的重载定义
complex operator+ ( complex & c1,complex & c2 )
{
float r = c1.real + c2.real;
float i = c1.imag + c2.imag;
return complex( r,i );
}
这时,要在类 complex定义中增加友元函数声明
friend complex operator+ ( complex &,complex & );
( b)对单目减,
complex operator- ( complex & c )
{
return complex( - c.real,- c.imag );
}
14
对象赋值与赋值运算符重载
赋值是一种极为普通的运算。当运算对象
不是基本类型时,应当给出运算符重载函
数。这时,两个同类对象之间的相互赋值
是逐个数据成员赋值。当赋值符重载函数
缺省时,C++编译器可以为每个类生成一
个缺省的赋值运算符重载函数
这种类对象的赋值,不需程序员自己去定
义赋值号的重载函数,便可以自由地使用
赋值号去实现同类对象间的赋值
15
类型转换与转换函数
在对表达式求值时,可能对操作对象进行
类型转换。同样,一个类对象运算的表达
式中也存在类型转换问题
转换有两种,
· 由其它类型转换为本类类型;
· 本类类型转换为其它类型
16
由其它类型转换
#include <iostream.h>
class complex{
private,
float real,imag;
public,
complex(float r = 0,float i = 0)
{
real = r;imag = i;
cout<<"Initializing with:"<< real<<','<< imag <<"\n";
}
void print()
{
cout << '('<<real << ',' << imag << ')'<< endl;
}
};
int main()
{ complex c1(2.3,4.5); // 创建一个 complex对象
c1.print();
c1 =,55; // 用 double类型常量向 complex对象赋值
c1.print();
c1 = complex(5); // 把 int型常量显式转换为 complex类型
c1.print();
return 0;
}
17
转换为其它类型
#include <iostream.h>
class complex{
private,
float real,imag;
public,
complex(float r = 0,float i = 0)
{ real = r;imag = i;
cout<<"Initializing...\n";
}
operator double() // 类型转换函数
{ cout<<"Type changed...\n";
return double(real);
}
void print()
{ cout <<'(' << real << ',' << imag <<')' << endl;
}
};
int main()
{ complex c1(2.3,4.5); // 创建对象 c1
c1.print();
cout<<(c1*3.33333333) << endl; //隐含地调用类型转换函数
return 0;
}
18
插入/提取符及其重载
C++把输出与输入看作数据的流动过程,
因此把输出数据与输入数据称为输出流与
输入流,对其操作称为插入与提取
预定义插入符
预定义提取符
插入/提取符重载
19
预定义插入符
C++系统预定义有一个 ostream类,用于管
理数据的输出,cout就是它的一个对象。
表达式
cout << i
将被 C++编译器解释为
cout.operator<< (i)
其中,i是传递给这个对象并启动其方法函
数 operator<<的一个消息
20
例 4.1.7
# include <iostream.h>
int main()
{
const char *s = "Hello,Borland C++!";
cout << s << endl;
cout <<(void *)s << endl;
return 0;
}
21
例 4.1.8
# include <iostream.h>
int main()
{
int fun();
cout << "The value of function fun() is:" << fun() << endl;
return 0;
}
int fun()
{
cout << "Sorry,I am first!"<< endl;
return 250;
}
22
预定义提取符
C++系统预定义有一个 istream类,用于管
理数据的输入,cin就是它的一个对象。 >>
称为提取运算符。 C++预定义的提取符是
类 istream的成员函数,原型为,
istream& istream,,operator>> (type & obj);
其中 type类型除不可为 void*之外,其余与
预定义的插入符相同
23
流的提取过程
(1) 对输入流对象 cin的提取操作,在用户
输入完数据并按下回车键之后才进行
(2) 输入的数据被放到输入缓冲区中
(3) 当一次输入的数据不足时,程序将等待
继续输入;当输入的数据(即缓冲区中的
数据)多余时,遇到下一次操作才被提取
(4) 输入流中数据的类型必须与提取变量的
类型一致,否则会导致提取失败
24
插入/提取符重载
class Fraction {
private,
int numerator; // 分子
int denominator; // 分母
public,
Fraction (int n=1,int m=1)
{ numerator=n; denominator=m;}
friend Fraction operator+(Fraction &,Fraction &);
friend Fraction operator-(Fraction &,Fraction &);
…
friend ostream & operator<<(ostream &,Fraction &);
friend istream & operator>>(istream &,Fraction &);
};
25
运算符重载规则
1) 不可以重载的运算符种类
2)类运算符重载与友元运算符重载方式
3)被重载的运算符必须保持预定义的优
先级与结合性
4)尽管重载可以改变运算符的语义
5)注意各运算符之间的联系
26
函数模板
类型参数化
函数模板( template函数)
异常处理
27
类型参数化
为了交换不同类型的数据的值,不得不定
义不同类型的数据交换函数,使用函数名
重载方法,根据实参的类型联编对应的函
数
使用类属 (genericity)。类属也称类型参数
化,它把类型定义为参数,从而实现了真
正的代码的可重用性
28
函数模板( template函数)
实际上不是一个完整的函数定义,因为
Type还是一个没有确定的类型
用它声明的只用以描述某角色在函数被操
作的形式。为了使用函数,必须将其模板
参数实例化。用模板实参实例化的函数也
称为模板函数
一种方法是显式地声明一个模板函数
一种方法是使用隐式的模板函数
29
显式地声明一个模板函数
template <class Type>
int isGreat(Type x,Type y)
{ return (x>y);}
void main()
{
int isGreat(float,float);
// 显式地声明一个模板函数,仅在 BC中有效
isGreat(2.5,3);
isGreat(3,5);
isGreat(3.6,5.2);
}
30
隐式的模板函数
template <class Type>
int isGreat(Type x,Type y)
{
return(x>y);
}
void main()
{
int i1 = isGreat(3,5); // 隐含有模板函数
int i2 = isGreat(2.35,3.56);
int i3 = isGreat('a','x');
…
}
31
例 4.2.2
#include <iostream.h>
#include <conio.h>
template <class T>
T Sum( T* array,int size = 0)
{
T total = 0;
for(int i = 0; i < size; i ++)
total += array[i];
return total;
}
int Intarr[] = {1,2,3,4,5,6,7,8,9,10};
double Douarr[] = {1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8,9.9,10.0};
void main()
{
int itotal = Sum(Intarr,10);
double dtotal = Sum(Douarr,10);
cout<<"The summary of integer array are:"<<itotal<<endl;
cout<<"The summary of double floating array are:"<<dtotal<<endl;
}
32
使用模板函数的通用排序程序
template <class T>
void genSort(T * base,int n)
{
int i,j;
for( i=1;i<n;i++)
for(j=0;j<n-i;j++)
if(base[j]>base[j+1])
{
T temp=base[j];
base[j]=base[j+1];
base[j+1]=temp;
}
}
33
使用重载函数时应特别注意
1)重载函数要求函数名相同
2)重载函数定义要不引起调用时的二义
性,如下边的两个定义将会引起调用时的
二义性
一般说来,重载函数主要解决函数名相同、
算法亦相同,但参数个数或类型不同的问
题
34
异常处理
#include <iostream.h>
#include <conio.h>
template <class Type>
Type max(Type v1,Type v2)
{
return v1>v2? v1:v2;
}
void main()
{
int m1 = max(100,300);
double m2 = max(32.1,3.142);
char * m3 = max("Zhang","Li");
cout << "The maxium of 100 and 300 is:" << m1 << endl;
cout << "The maxium of 32.1 and 3.142 is:" << m2 << endl;
cout << "The maxium of Zhang and Li" <<"is,"<< m3 << endl;
}
35
类模板
类模板的定义
类模板中的友元函数
异常处理
36
类模板
对象是类的特例,类又可以看作类模板的
特例。类模板也称类属类或类发生器,它
们与函数模板有同样的应用目的,但也有
诸多不同
主要的不同是,
( 1)对由函数模板生成的模板函数的调
用是由编译器自动决定的,而对类模板的
解释由程序设计者自行指明;
( 2)格式定义上也有许多不同。
37
类模板的定义
// 一个定义类模板和介绍容器的例子
#include <iostream.h>
#include <iomanip.h>
#include <conio.h>
const int AS=20; // 定义数组大小
// 接口定义
template <class Type>
class Array
{
private,
Type * element;
int size;
public,
Array ( const int size );
~ Array ();
Type & operator[] ( const int index );
void operator = (Type temp);
};
38
template < class Type > inline // 实现
Array < Type >,,Array ( const int s )
{
size=s;
element=new Type[size];
for(int i=0;i<size;i++)
element[i]=0;
}
template<class Type>inline
Array < Type >:,~ Array( )
{
delete element;
}
template < class Type >
Type& Array < Type >::operator[ ] ( const int index )
{
return element [ index ];
}
template<class Type>
void Array<Type>::operator=(Type temp)
{
for(int i=0;i<size;i++)
element[i]=temp;
}
39
定义类模板时应注意
1) 定义一个类模板与定义一般类函数模板
的格式相同,必须以关键字 template开始,
后面是尖括号起的类型参表列,然后是类
名
2) 每一个类模板的成员函数的外部定义,
都与一般函数模板的定义一样,必须以类
型形参开始,此外还应加以函数返回值类
型、类范围、函数名、形参序列
3) 类模板是类的抽象类型,它的实例化是
生成一个具体的类
40
类模板中的友元函数
在类模板中可以声明三种友元函数,
· 一般的友元函数,这种友元函数中不使
用任何类模板中的类型参数表列中的参数。
· 封闭型类模板友元函数 (bound template
friend function)。
· 开放型类模板友元函数 (unboumd template
friend funcfion)
41
封闭型类模板友元函数
用这种格式定义或声明的友元函数模板中
要使用类模板中统一给定的类型参数
在实例化时,对每一种类类型都会生成一
个相对应的模板友元函数
42
开放型类模板友元函数
这是一种允许定义自己的类型参数的友元
函数
为了让友元函数有自己的类型参数,每个
友元函数前都应有自己的类型参数定义
这些友元函数模板中的类型参数不必与类
模板类型参数相同
43
例 4.3.2
#include <iostream.h>
#include <conio.h>
const int AS=8; //定义数组大小
template <class Type>
class Array //接口定义
{
friend ostream& operator<<(ostream& os,Array<Type> & array);
private,
Type* element;
int size;
public,
Array(const int size);
~Array();
Type& operator[] (const int index);
void operator=(Type temp);
};
template <class Type> inline // 实现
Array<type>::Array (const int s)
{
size=s;
element=new Type[size];
for (int i=0;i<size;i++)
element[i]=0;
}
44
template<class Type>inline
Array<Type>::~Array()
{
delete element;
}
template <class Type>
Type& Array<Type>::operator[] (const int index)
{
return element[index];
}
template<class Type>
void Array<Type>::operator=(Type temp)
{
for(int i=0;i<size;i++)
element[i]=temp;
}
template <class Type>
ostream& operator<< (ostream& os,Array <Type> & array) // 友元函数
{
for (int i=0;i<array.size;i++)
os<<array.element[i]<<endl;
return os;
}
45
异常处理
异常处理用以处理不能按例行规则进行一
般处理的情况。与函数模板一样,类模板
被实例化时也会出现某部分的成员函数无
法适应某个数据类型的异常情况
在这种情况下可以用下面的一种方法解决,
1)为需要异常处理的数据类型重设一新
的特别成员函数
2)为类模板重设一特别的类,如重设一
个专门处理 char *类型数据的 Array类
46
处理 char *类型数据的 Array类
class Array <char *>
{
friend ostream& operator<<(ostream &os,Array<char *> &array);
private,
char ** element;
int size;
public,
Array(const int size);
~ Array ();
char*& operator[] (const int index);
void operator= ( char* temp );
};
47
类模板重设异常处理类注意
1) template关键字与类型参数序列不必再
使用,但类名后必须加上已定义的数据类
型以表明此类是专门处理某型态的特殊类
2)特别类的定义不必与原来 template类中
的定义完全相同
48
习题
第 4章 重载与模板
2
第 4章 重载与模板
函数名重载
函数模板
类模板
3
函数名重载
函数名重载与静态绑定
对象赋值与赋值运算符重载
类型转换与转换函数
插入/提取符及其重载
运算符重载规则
4
函数名重载与静态绑定
函数名重载
运算符重载
运算符重载的成员函数方式和友元函数方
式
5
函数名重载与静态绑定
绑定( binding)又称联编、聚束,是指程
序在编译或运行时将一个标识解释为一个
相应的程序实体
这里讲的绑定,主要指程序在编译或运行
时将一个函数名解释为一个相应的函数体
前面介绍的函数都是一个函数名对应一个
函数体。 C++也允许一个函数名对应多个
函数体,形成函数名的多态性。这种多态
性可以简单地概括为, 一个接口,多种方
法,
6
函数名重载
函数名重载就是多个函数使用一个函数名
先看下面的例子。
例 4.1.1 对任意两个数据变量或字符变量
进行交换
7
void swap(char & n1,char & n2) // 定义( 1)
{
char temp = n1;
n1 = n2;
n2 = temp;
}
void swap(int & n1,int & n2) // 定义( 2)
{
int temp = n1;
n1 = n2;
n2 = temp;
}
void swap(float & n1,float & n2) // 定义( 3)
{
float temp = n1;
n1 = n2;
n2 = temp;
}
void swap(double & n1,double & n2) // 定义( 4)
{
double temp = n1;
n1 = n2;
n2 = temp;
}
8
运算符重载
系统只预定义了少数几个用于基本类型的
运算符函数。对于更多的类型,需要程序
员自己去定义相应的运算符重载函数。通
过运算符重载,使已有的运算符可以用于
各种对象
9
运算符重载
用户定义类型的重载运算符,要求能访问
运算对象的私有成员
为此只能用
成员函数
友元函数
两种形式定义运算符重载
10
表达式 成员函数方式 友元函数方式
一元运算表达式,obj @
@ obj
obj.operator@ (0 );
obj.operator@( )
operator@(obj,0)
operator @ ( obj )
二元运算表达式,obj1 @ obj2 obj1.operator@ ( obj2
);
operator @ ( obj1,obj2 )
11
复数类重载两个类运算符
# include <iostream.h>
class complex{
private,
float real,// 实部
imag; // 虚部
public,
complex(float r = 0,float i = 0)
{
real = r; imag = i;
}
void print()
{
cout << "c(" << real << ',' << imag << ')' << endl;
}
};
12
成员函数方式
( a)对单目减,
complex /*返回类型 */ complex /*类名 */,,operator- ( )
{
return complex(-real,-imag);
}
( b)对双目加,
complex /*返回类型 */ complex /*类名 */,,opelater+ ( complex &c )
{
float r = this -> real + c.real;
float i = this -> imag + c.imag;
return complex(r,i); // 返回一个新对象,这时将调用构造函数
}
13
友元函数方式
( a)友元函数方式的双目 +的重载定义
complex operator+ ( complex & c1,complex & c2 )
{
float r = c1.real + c2.real;
float i = c1.imag + c2.imag;
return complex( r,i );
}
这时,要在类 complex定义中增加友元函数声明
friend complex operator+ ( complex &,complex & );
( b)对单目减,
complex operator- ( complex & c )
{
return complex( - c.real,- c.imag );
}
14
对象赋值与赋值运算符重载
赋值是一种极为普通的运算。当运算对象
不是基本类型时,应当给出运算符重载函
数。这时,两个同类对象之间的相互赋值
是逐个数据成员赋值。当赋值符重载函数
缺省时,C++编译器可以为每个类生成一
个缺省的赋值运算符重载函数
这种类对象的赋值,不需程序员自己去定
义赋值号的重载函数,便可以自由地使用
赋值号去实现同类对象间的赋值
15
类型转换与转换函数
在对表达式求值时,可能对操作对象进行
类型转换。同样,一个类对象运算的表达
式中也存在类型转换问题
转换有两种,
· 由其它类型转换为本类类型;
· 本类类型转换为其它类型
16
由其它类型转换
#include <iostream.h>
class complex{
private,
float real,imag;
public,
complex(float r = 0,float i = 0)
{
real = r;imag = i;
cout<<"Initializing with:"<< real<<','<< imag <<"\n";
}
void print()
{
cout << '('<<real << ',' << imag << ')'<< endl;
}
};
int main()
{ complex c1(2.3,4.5); // 创建一个 complex对象
c1.print();
c1 =,55; // 用 double类型常量向 complex对象赋值
c1.print();
c1 = complex(5); // 把 int型常量显式转换为 complex类型
c1.print();
return 0;
}
17
转换为其它类型
#include <iostream.h>
class complex{
private,
float real,imag;
public,
complex(float r = 0,float i = 0)
{ real = r;imag = i;
cout<<"Initializing...\n";
}
operator double() // 类型转换函数
{ cout<<"Type changed...\n";
return double(real);
}
void print()
{ cout <<'(' << real << ',' << imag <<')' << endl;
}
};
int main()
{ complex c1(2.3,4.5); // 创建对象 c1
c1.print();
cout<<(c1*3.33333333) << endl; //隐含地调用类型转换函数
return 0;
}
18
插入/提取符及其重载
C++把输出与输入看作数据的流动过程,
因此把输出数据与输入数据称为输出流与
输入流,对其操作称为插入与提取
预定义插入符
预定义提取符
插入/提取符重载
19
预定义插入符
C++系统预定义有一个 ostream类,用于管
理数据的输出,cout就是它的一个对象。
表达式
cout << i
将被 C++编译器解释为
cout.operator<< (i)
其中,i是传递给这个对象并启动其方法函
数 operator<<的一个消息
20
例 4.1.7
# include <iostream.h>
int main()
{
const char *s = "Hello,Borland C++!";
cout << s << endl;
cout <<(void *)s << endl;
return 0;
}
21
例 4.1.8
# include <iostream.h>
int main()
{
int fun();
cout << "The value of function fun() is:" << fun() << endl;
return 0;
}
int fun()
{
cout << "Sorry,I am first!"<< endl;
return 250;
}
22
预定义提取符
C++系统预定义有一个 istream类,用于管
理数据的输入,cin就是它的一个对象。 >>
称为提取运算符。 C++预定义的提取符是
类 istream的成员函数,原型为,
istream& istream,,operator>> (type & obj);
其中 type类型除不可为 void*之外,其余与
预定义的插入符相同
23
流的提取过程
(1) 对输入流对象 cin的提取操作,在用户
输入完数据并按下回车键之后才进行
(2) 输入的数据被放到输入缓冲区中
(3) 当一次输入的数据不足时,程序将等待
继续输入;当输入的数据(即缓冲区中的
数据)多余时,遇到下一次操作才被提取
(4) 输入流中数据的类型必须与提取变量的
类型一致,否则会导致提取失败
24
插入/提取符重载
class Fraction {
private,
int numerator; // 分子
int denominator; // 分母
public,
Fraction (int n=1,int m=1)
{ numerator=n; denominator=m;}
friend Fraction operator+(Fraction &,Fraction &);
friend Fraction operator-(Fraction &,Fraction &);
…
friend ostream & operator<<(ostream &,Fraction &);
friend istream & operator>>(istream &,Fraction &);
};
25
运算符重载规则
1) 不可以重载的运算符种类
2)类运算符重载与友元运算符重载方式
3)被重载的运算符必须保持预定义的优
先级与结合性
4)尽管重载可以改变运算符的语义
5)注意各运算符之间的联系
26
函数模板
类型参数化
函数模板( template函数)
异常处理
27
类型参数化
为了交换不同类型的数据的值,不得不定
义不同类型的数据交换函数,使用函数名
重载方法,根据实参的类型联编对应的函
数
使用类属 (genericity)。类属也称类型参数
化,它把类型定义为参数,从而实现了真
正的代码的可重用性
28
函数模板( template函数)
实际上不是一个完整的函数定义,因为
Type还是一个没有确定的类型
用它声明的只用以描述某角色在函数被操
作的形式。为了使用函数,必须将其模板
参数实例化。用模板实参实例化的函数也
称为模板函数
一种方法是显式地声明一个模板函数
一种方法是使用隐式的模板函数
29
显式地声明一个模板函数
template <class Type>
int isGreat(Type x,Type y)
{ return (x>y);}
void main()
{
int isGreat(float,float);
// 显式地声明一个模板函数,仅在 BC中有效
isGreat(2.5,3);
isGreat(3,5);
isGreat(3.6,5.2);
}
30
隐式的模板函数
template <class Type>
int isGreat(Type x,Type y)
{
return(x>y);
}
void main()
{
int i1 = isGreat(3,5); // 隐含有模板函数
int i2 = isGreat(2.35,3.56);
int i3 = isGreat('a','x');
…
}
31
例 4.2.2
#include <iostream.h>
#include <conio.h>
template <class T>
T Sum( T* array,int size = 0)
{
T total = 0;
for(int i = 0; i < size; i ++)
total += array[i];
return total;
}
int Intarr[] = {1,2,3,4,5,6,7,8,9,10};
double Douarr[] = {1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8,9.9,10.0};
void main()
{
int itotal = Sum(Intarr,10);
double dtotal = Sum(Douarr,10);
cout<<"The summary of integer array are:"<<itotal<<endl;
cout<<"The summary of double floating array are:"<<dtotal<<endl;
}
32
使用模板函数的通用排序程序
template <class T>
void genSort(T * base,int n)
{
int i,j;
for( i=1;i<n;i++)
for(j=0;j<n-i;j++)
if(base[j]>base[j+1])
{
T temp=base[j];
base[j]=base[j+1];
base[j+1]=temp;
}
}
33
使用重载函数时应特别注意
1)重载函数要求函数名相同
2)重载函数定义要不引起调用时的二义
性,如下边的两个定义将会引起调用时的
二义性
一般说来,重载函数主要解决函数名相同、
算法亦相同,但参数个数或类型不同的问
题
34
异常处理
#include <iostream.h>
#include <conio.h>
template <class Type>
Type max(Type v1,Type v2)
{
return v1>v2? v1:v2;
}
void main()
{
int m1 = max(100,300);
double m2 = max(32.1,3.142);
char * m3 = max("Zhang","Li");
cout << "The maxium of 100 and 300 is:" << m1 << endl;
cout << "The maxium of 32.1 and 3.142 is:" << m2 << endl;
cout << "The maxium of Zhang and Li" <<"is,"<< m3 << endl;
}
35
类模板
类模板的定义
类模板中的友元函数
异常处理
36
类模板
对象是类的特例,类又可以看作类模板的
特例。类模板也称类属类或类发生器,它
们与函数模板有同样的应用目的,但也有
诸多不同
主要的不同是,
( 1)对由函数模板生成的模板函数的调
用是由编译器自动决定的,而对类模板的
解释由程序设计者自行指明;
( 2)格式定义上也有许多不同。
37
类模板的定义
// 一个定义类模板和介绍容器的例子
#include <iostream.h>
#include <iomanip.h>
#include <conio.h>
const int AS=20; // 定义数组大小
// 接口定义
template <class Type>
class Array
{
private,
Type * element;
int size;
public,
Array ( const int size );
~ Array ();
Type & operator[] ( const int index );
void operator = (Type temp);
};
38
template < class Type > inline // 实现
Array < Type >,,Array ( const int s )
{
size=s;
element=new Type[size];
for(int i=0;i<size;i++)
element[i]=0;
}
template<class Type>inline
Array < Type >:,~ Array( )
{
delete element;
}
template < class Type >
Type& Array < Type >::operator[ ] ( const int index )
{
return element [ index ];
}
template<class Type>
void Array<Type>::operator=(Type temp)
{
for(int i=0;i<size;i++)
element[i]=temp;
}
39
定义类模板时应注意
1) 定义一个类模板与定义一般类函数模板
的格式相同,必须以关键字 template开始,
后面是尖括号起的类型参表列,然后是类
名
2) 每一个类模板的成员函数的外部定义,
都与一般函数模板的定义一样,必须以类
型形参开始,此外还应加以函数返回值类
型、类范围、函数名、形参序列
3) 类模板是类的抽象类型,它的实例化是
生成一个具体的类
40
类模板中的友元函数
在类模板中可以声明三种友元函数,
· 一般的友元函数,这种友元函数中不使
用任何类模板中的类型参数表列中的参数。
· 封闭型类模板友元函数 (bound template
friend function)。
· 开放型类模板友元函数 (unboumd template
friend funcfion)
41
封闭型类模板友元函数
用这种格式定义或声明的友元函数模板中
要使用类模板中统一给定的类型参数
在实例化时,对每一种类类型都会生成一
个相对应的模板友元函数
42
开放型类模板友元函数
这是一种允许定义自己的类型参数的友元
函数
为了让友元函数有自己的类型参数,每个
友元函数前都应有自己的类型参数定义
这些友元函数模板中的类型参数不必与类
模板类型参数相同
43
例 4.3.2
#include <iostream.h>
#include <conio.h>
const int AS=8; //定义数组大小
template <class Type>
class Array //接口定义
{
friend ostream& operator<<(ostream& os,Array<Type> & array);
private,
Type* element;
int size;
public,
Array(const int size);
~Array();
Type& operator[] (const int index);
void operator=(Type temp);
};
template <class Type> inline // 实现
Array<type>::Array (const int s)
{
size=s;
element=new Type[size];
for (int i=0;i<size;i++)
element[i]=0;
}
44
template<class Type>inline
Array<Type>::~Array()
{
delete element;
}
template <class Type>
Type& Array<Type>::operator[] (const int index)
{
return element[index];
}
template<class Type>
void Array<Type>::operator=(Type temp)
{
for(int i=0;i<size;i++)
element[i]=temp;
}
template <class Type>
ostream& operator<< (ostream& os,Array <Type> & array) // 友元函数
{
for (int i=0;i<array.size;i++)
os<<array.element[i]<<endl;
return os;
}
45
异常处理
异常处理用以处理不能按例行规则进行一
般处理的情况。与函数模板一样,类模板
被实例化时也会出现某部分的成员函数无
法适应某个数据类型的异常情况
在这种情况下可以用下面的一种方法解决,
1)为需要异常处理的数据类型重设一新
的特别成员函数
2)为类模板重设一特别的类,如重设一
个专门处理 char *类型数据的 Array类
46
处理 char *类型数据的 Array类
class Array <char *>
{
friend ostream& operator<<(ostream &os,Array<char *> &array);
private,
char ** element;
int size;
public,
Array(const int size);
~ Array ();
char*& operator[] (const int index);
void operator= ( char* temp );
};
47
类模板重设异常处理类注意
1) template关键字与类型参数序列不必再
使用,但类名后必须加上已定义的数据类
型以表明此类是专门处理某型态的特殊类
2)特别类的定义不必与原来 template类中
的定义完全相同
48
习题