函数重载和运算符重载

函数重载是静态多态的实现方式,通过函数重载我们可以通过相同的函数名执行不同的操作,而运算符重载虽然形式上和函数重载区别很大,但是运算符重载本质也是定义一个函数来完成特定操作,所以运算符重载也能作为特殊的函数重载。

既然被重载的函数具有相同的函数名,那么准确区分调用哪一个函数就需要通过别的特征来判断。在函数声明中,除了函数名之外还有返回值类型和参数列表。在C++中重载的关键就是函数的参数列表,如果两个函数参数列表中类型、顺序和数量均相同的话,则它们的表征相同,至于变量名和返回值类型则不能用于区分重名函数。

可以预见,程序在编译阶段就会通过参数类型而选择特定的函数,在调用时,多个同名函数通过函数参数列表的不同进行匹配。

但是在重载中有些陷阱需要特别注意,这些地方会造成理解的偏差并导致程序最终出错,也是我们必须注意的点。

  1. 编译器在通过参数列表检查函数特征时,会把类型引用和类型本身视为同一个特征。比如下面两个原型是不加区分的:

void print(int x);

void print(int &x);

 

  1. 编译器不区分const变量和非const变量。比如下面两个例子。

void print(int x);

void print(const int x);

如果我们定义两个字符串作为输入参数的话,默认会把const类型的变量与加了const类型的函数匹配,非const类型变量与非const类型函数匹配。但如果只有const类型函数,此时传入非const类型变量依旧可以完成匹配,反之却不成立,因为我们把一个const类型变量传给非const类型变量是非法的。

因为函数重载本身没有什么太大难点,所以这里简单总结一下。判断是否为重载函数只要确定两点即可:1.函数名相同;2.具有不同的参数列表,至于返回值是什么类型并不会作判断。另外,我们从编译器角度来看函数重载的问题也需会更加深刻一些,C++的编译器在编译时会对函数名进行简单的重新编码,根据参数的类型和个数进行加密,因为重载函数的参数列表各不相同,所以编译过程中对函数名加密的过程相当于重新给这些函数赋予了不同的函数名,这也就是为什么函数看起来函数名相同却能通过编译的原因。

接下来讨论下运算符重载的问题。其实运算符重载和函数重载本质上没有区别,通俗来说都是根据实际操作数据的类型进行“个性化定制”服务,但是对外提供统一的接口形式,比如在函数重载时函数名是完全相同的,调用时不用记忆过多的函数名,只需要把所需操作的数据当做参数即可,系统会自动调用所需的操作。运算符重载的作用也是异曲同工,举个简单例子,通常我们定义的“+”操作是对两个数字进行的,但是如果我们想在想进一步拓展“+”运算符的功能,比如拼接两个字符串或者对两个对象相加的话,就需要通过运算符重载来为之前只有基本功能的运算符添加新功能。

运算符重载的基本形式为:operator op (argument-list)

我们举一个“时间类”相加的例子来解释运算符重载。

#include <iostream>

using namespace std;

class Time

{

private:

int hours;

int minutes;

public:

Time(){}

Time(int h, int m);

Time operator +(const Time &t)const;

~Time(){}

void show()const;

};

Time::Time(int h, int m)

{

hours = h;

minutes = m;

}

Time Time::operator +(const Time &t) const

{

Time sum;

sum.minutes = minutes + t.minutes;

sum.hours = hours + t.hours + sum.minutes / 60;

sum.minutes = sum.minutes % 60;

return sum;

}

void Time::show() const

{

cout << hours << ” hours ” << minutes << ” minutes ” << endl;

}

int main()

{

Time a(2, 30);

Time b(4, 55);

b = a + b;

b.show();

return 0;

}

在这个例子中,对加法运算符的功能进行了扩展,使其能够根据运算符两端操作数的类型对应相应的操作。在上例中,因为加法两边的变量是Time类的对象,所以就调用之前定义的类的加法进行计算,当然,如果我们仅仅是对两个数字进行相加时,这里的加法运算符就会退化到最基本的功能。

可见,运算符重载和函数重载的机理非常相似,对外均是提供统一的函数接口,之后通过参数或者操作数类型,将其映射到相应的操作函数上去。这里相比于虚函数所有过程都是系统自动完成有些不同,虽然我们在调用时候同样不在需要关心接口的名字,但是具体传入什么参数或者用什么类型操作数其实都是可控的,虽然得到参数后对应到具体哪个函数来执行这个步骤是系统帮助我们完成的。

那么,运算符重载又没有什么限制呢?有的,包括下列几点。

  1. 至少有一个操作数是用户定义的数据类型。
  2. 不能违反原来的句法规则,比如用对加法运算符实现减法操作。
  3. 不能修改原运算符优先级,比如同时对“*”和“+”重载,在计算时乘法依旧优先级要高于加法。
  4. 不能创建原本不存在的运算符。
  5. 不能重载以下运算符:“sizeof”、“.”、“*”、“::”和“?:”。
  6. 以下四种运算符只能以成员函数形式进行函数重载:“[]”、“()”、“=”和“->”。

所以总结起来就是:我们只能对C++大部分原生的运算符重载,有五个不能重载,有四个只能以成员函数形式重载,重载时不能违背原意且保持C++中这些运算符优先级不变。关于通过重载方式实现静态多态就讨论到这里。

 

 

Advertisements