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
习题