引言
重载的本质
重载(overloading)是同一符号在不同作用域的不同应用条件下具有不同的语义。这个符号可以是函数,运算符,也就是一物多用
函数重载
函数重载:定义同名但参数类型与数量不同的函数,编译系统根参数的匹配规则来决定调用哪一个函数
前置内容(带缺省值的函数)
带缺省值的函数
函数在说明或函数定义中,形参指定了一个数值,则称此函数为带缺省值的函数,指定的值称为缺省值
Example
1
2
3
|
//带缺省值的函数声明:
float area(float r=6.5)
//6.5就是缺省值
|
在调用时,如果不传参数,使用默认值,就会默认形参等于缺省值
不使用默认值
带多个缺省参数的函数
如果函数有多个缺省参数,则缺省参数必须是 从右向左定义,并且在一个缺省参数的右边不能有 未指定缺省值的参数。(缺省值集中在参数表右侧)
1
2
|
void f1( float a , int b=6 , int c , char d='a' );//错误定义
void f2( float a , int c , int b=6 , char d='a' );//正确定义
|
调用形式可以为:
1
2
3
|
f2( 3.5 , 5 , 3 , 'x' );
f2( 3.5 , 5 , 3 );
f2( 3.5 , 5 );
|
不带缺省的得函数重载
函数重载:定义同名但参数类型与数量不同的函数,编译系统根据参数的匹配规则来来决定调用哪一个函数
1
2
3
4
5
6
7
8
9
|
int max( int a , int b , int c ) {
...
}
int max( int a , int b ) {
...
}
double max(double a , double b ) {
...
}
|
注意:不能出现函数参数个数,参数类型都相同,只有函数返回值不同的同名函数。
因为系统将无法从调用形式上判断调用哪个函数
1
2
3
4
5
6
|
int max( int a , int b ) {
...
}
bool max( int a , int b ) {
...
}
|
带缺省值的函数重载
一般带缺省值的函数不能进行重载,容易出现错误
错误实例:
1
2
3
4
5
6
7
8
9
10
|
int max( int a , int b , int c=1 ) {
...
}
int max( int a , int b ) {
...
}
int main() {
max(1,2) ;
}
|
此时系统无法匹配正确的函数,不能实现函数重载(静态多态)
一个函数不能即作为重载函数,又 作为有默认参数的函数!
(这是一个建议,其实并不绝对,只要让系统能匹配到唯一的函数就行)
运算符重载
前置内容(C++ 类与对象)
运算符重载本质上是函数重载,将指定的运算表达式转化为对运算符函数的调用,运算对象转化为运算符函数的实参(看个乐呵就行)
为什么需要运算符重载
C++中预定义的运算符其运算对象只能是基本数据类型,而不适用于用户自定义类型。运算符重载允许把C++标准运算符应用于自定义数据类型的对象,与函数重载类似,对已有的运算符赋予新的含义,用一个运算符表示不同功能的运算, 这就是运算符重载
运算符重载的例子
类的定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class complex {
public:
complex();//默认构造函数
complex(double,double);//初始化构造函数
complex(double);//强制转换构造函数
//*****************************************************//
//下面是二选一
complex operator + (complex&);//运算符重载成员函数实现
complex operator - (complex&);//运算符重载成员函数实现
friend complex operator + (complex&,complex&);//友元函数实现
friend complex operator - (complex&,complex&);//友元函数实现
//****************************************************//
friend istream& operator >> (istream& , complex&);//istream类>>运算符重载
friend ostream& operator << (ostream& , complex&);//ostream类<<运算符重载
private:
double real , imag ;
};
|
成员函数重载运算符
隐含this指针,左侧自定义类型
翻译为
参数个数=原操作数个数-1(后置++、–除外)
模板
1
2
3
|
数据类型(返回值类型) 类名(域)::operator 运算符( 形参表 ) {
重载处理
}
|
具体例子
1
2
3
4
5
6
7
8
9
10
11
12
13
|
complex complex::operator + (const complex &c1) {
complex c2 ;
c2.real = real+c1.real ;
c2.imag = imag+c1.imag ;
return c2 ;
}
complex complex::operator - (const complex &c1) {
complex c2 ;
c2.real = real-c1.real ;
c2.imag = imag-c1.imag ;
return c2 ;
}
|
友元函数重载运算符
翻译为
1
|
c3 = operator+(c1,c2);//友元函数
|
参数个数=原操作数个数 (至少有一个自定义类型形参)
模板
1
2
3
|
friend 数据类型(返回值类型) 类名(域):: operator 运算符( 形参表 ) {
重载处理
}
|
具体例子
1
2
3
4
5
6
7
8
9
10
11
12
13
|
complex complex::operator + (const complex &c1 , const complex &c2) {
complex c ;
c.real = c2.real+c1.real ;
c.imag = c2.imag+c1.imag ;
return c ;
}
complex complex::operator - (const complex &c1 , const complex &c2) {
complex c ;
c.real = c1.real-c2.real ;、
c.imag = c1.imag-c2.imag ;
return c ;
}
|
重载«和»运算符
«重载函数和»重载函数只能定义为友元函数,不能定义为成员函数,
因为«和»运算符有两个形参,并且第一个形参不是自定义类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//类中的友元函数定义
//***********************************//
friend istream& operator >> (istream& , complex&);//istream类>>运算符重载
friend ostream& operator << (ostream& , complex&);//ostream类<<运算符重载
//***********************************//
//复数的自定义输入输出
istream& operator >> (istream& input , complex&c ) {
input >> c.real >> c.imag ;
return input ;
}
ostream& operator << (ostream& output , complex&c ) {
output << c.real << '+' << c.imag << 'i' << endl ;
return output ;
}
|
重载单目运算符
成员函数重载
前置++
1
2
|
//前置
complex operator ++();
|
后置++
1
2
3
|
//后置
complex operator ++(int);
//如果在形参中添加int占位参数,编译器会识别出这是后置++
|
友元函数重载
前置++
1
2
|
//前置
friend complex operator ++ (complex);
|
后置++
1
|
friend complex operator ++ (complex,int);
|
运算符重载的规则(看个乐呵就行)
• 运算符重载不允许发明新的运算符。如@
• 不能改变运算符操作对象的个数,如+是双目运算
• 运算符被重载后,优先级和结合性不会改变
• 经重载的运算符至少有一个参数为自定义类型,否则系 统已经实现
运算符重载的实现形式
运算符重载函数可以声明为 类成员函数 也可以是 类的友元函数 ,还可以是 普通函数
推荐使用成员函数或是友元函数实现
(主要因为私有成员的访问问题)
实现运算符重载的方式——既然是操作符重载,一般会访问类的私有成员变量,根据类的封装性要求,除了友元函数外,其他任何外部操作都是违规的,所以不推荐用 普通函数 来重载操作符