详解C++中函数重载与操作符重载

详解C++中函数重载与操作符重载

C++对C语言进行了巨大的扩展,其中重载就是其中之一,重载概念的由来是面向对象编程的思想延申,把现实生活中的东西融入到编程语言。重载相当于自然语言中的词汇搭配,打球,打篮球,打羽毛球,打乒乓球,打字与不同的名词结合产生不同意思,重载使得C++拥有丰富的语义表达能力。要时刻知道的是,函数重载的本质是不同的函数,C++编译器在背后做了很多的事情。现在我们就先来逐一解析函数重载吧。


#include <iostream>
using namespace std;

void func()	// 函数名为func()的函数
{
	cout << "func()" << endl;
}

void func(int i)	// 重载的func
{
	cout << i << "from" << "func(int i)" << endl;	
}


int main(int argc,char* argv[])
{
	func();
	func(1);

	return 0;
}

从上面的示例代码明显可以看出函数名相同,但是重载的func函数带有一个int 型的参数。其实,这就是C++中函数重载的关键点。注意!!!一般而言,重载函数名相同,但是函数参数必须不同,这个不同表现在:参数类型不同、参数个数不同、参数顺序不同,只要符合其中之一,那么他们就是不同的函数。嘿嘿,先跳过一个点。
但是,参数相同就真的可以重载了吗?比如C++中的默认值的使用。还是使用上面的示例代码,稍作修改。


#include <iostream>
using namespace std;

void func()
{
	cout << "func()" << endl;
}

void func(int i = 0)
{
	cout << i << "from" << "func(int i = 0)" << endl;
}

int main(int argc,char* argv[])
{
	func();		// 编译器不知道怎么办了
	func(1);
	
	return 0;
}

是不是有点好奇,编译器会告诉你,candidate(候选人)有两个,ambiguous,模棱两可的。意思就是:对不起,编译不通过!!!

编译器

那要是类型不同了,一定就可以重载了吗?使用同一段代码,拥有千变万化的能力的C++,在刷新你的想象。

#include <iosteam>
using namespace std;

void func(double d)
{
	cout << "func(double d)" << endl;
}

void func(double& d)
{
	cout << "func(double& d)" << endl;
}

int main(int argc.char* argv[])
{
	double d = 0.001;
	func(d);
	
	return 0;
}

上面的示例代码参数类型不同了,一个是double类型,一个是double&引用类型,如果直接使用常量作为参数,编译可以通过,因为编译器清楚地知道该调用哪个函数。这里使用的是变量来传递参数,编译器又懵了。Ambiguous!!!
总结起来就是,函数重载的一条规则就是,在程序运行的时候,必须仅有一个匹配的函数,但凡有多个匹配得上的编译器就会报错。
延续刚才的伏笔,跳过了函数定义中很重要的一个点,那就是返回值。返回值作为函数定义的重要部分,通常用于返回计算的结果,是判断程序是否正确运行的关键。看起来返回值应该作为函数重载的一条规则。不过事与愿违,C++并没有这样做。


#include <iostream>
using namespace std;

void func()
{
	cout << "func()" << endl;
}
int func()
{
	cout << "func()" << endl;
	return 0;
}

int main(int argc,char* argv[])
{
	return 0;
}

在这里插入图片描述
编译器说新的函数声明跟旧的矛盾。那函数重载跟函数的返回值就毫无关系了吗?勤思好学的你肯定想到了函数指针,在C/C++的世界,指针是逢人必讲,逢文必讲。函数指针可不知道你是重载还是什么,只要你的函数类型不匹配,那打扰了,编译不过。这里有一个小知识点要细细说明,在C++中函数参数为空就是圆括号里为空,而在C语言中却代表无限参数。


void func(){};	// 函数类型为:参数为空,返回值为空
void (*p1)();	// 指针类型为:指向的函数类型参数为空,返回值也为空

int func(){};	// 函数类型为:参数为空,返回值为int
int (*p2)();	// 指针类型为:指向的函数类型参数为空,返回值为int

int func(int i,int j){};	// 函数类型为:两个int参数,返回值为int
int (*p3)(int,int);		// 指针类型为:指向的函数类型有两个int参数返回值为int

需要强调的一点是,函数指针必须严格匹配,返回值和参数一个都不能错。当然啦,类的成员函数也可以进行重载,其规则与全局函数的重载一致。但有一点要说明的是,编译器优先调用成员函数。

是否意犹未尽?再接再厉把操作符重载也学一下。操作符重载就是用特殊形式的函数来扩展操作符的功能,简单地说,操作符重载就是让程序员可以天马行空尽情发挥(可以乱来),比如重载+操作符后,a+b进行的是a-b;言归正传,重载操作符主要是为类类型提供更方便快捷的编程方式,比如STL提供的string类,字符串类,类中提供了相当丰富的操作符重载函数供使用,最常用的有+操作符的重载,使得字符串的拼接更为方便。例如:

#include <string>
using namespace std;

string s1 = "hello";
string s2 = "world";

string s3 = s1 + s2;	// s3 = "helloworld"; 

能够重载的操作符只能是语言系统预定义的操作符,最常用的+ - * /,这些操作符可以重载为全局函数,如果此时操作符重载函数的参数是类对象,一般而言,都需要将该全局函数声明为类的友元(friend),为了简便,通常将操作符重载函数作为类的成员函数来实现,并且还可以减少一个参数(左操作数),还不用声明为友元,一举两得,快哉快哉。先来个全局重载试试水。


#include <iostream>
using namespace std;

class Complex
{
private:
	int x;
	int y;
public:
	Complex(int x = 0;int y = 0)
	{
		this->x = x;
		this->y = y;
	}
	
	int getX()
	{
		return x;
	} 
	
	int getY()
	{
		return y;
	}
	
	friend Complex operator +(const Complex& c1,const Complex& c2);
};

Complex operator +(const Complex& c1,const Complex& c2)
{
	Complex ret;
	ret.x = c1.x + c2.x;
	ret.y = c1.y + c2.y;

	return ret;
}

int main(int argc,char* argv[])
{
	Complex c1(1,2);
	Complex c2(3,4);

	Complex c3 = operator+(c1,c2);	// 以函数调用的方式

	cout << "c3.x = " << c3.getX << endl;
	cout << "c3.y = " << c3.getY << endl;
	
	Complex c4 = c2 + c3;		// 编译器自动推导,
	
	cout << "c4.x = " << endl;
	cout << "c4.y = " << endl;

	return 0;
}

写成类的成员函数效果一样,此处+操作符的类成员函数重载一笔掠过。下面将介绍只能重载为类成员函数的操作符,这些操作符有[],(),->,=,* 等,这里就拿[]数组操作符来做讲解,其实用法都差不都,依照操作符的原生语义进行编程就完事了。重载数组操作符只能带一个参数,没有规定参数类型。上代码:

#include <iostream>
#include <string>
#define ArraySize 5	// 大小随意修改

using namespace std;

class Array
{
public:
	int MyArray[ArraySize];
	
	Array()
	{
		for(int i = 0; i < ArraySize; ++ i)
		{
			MyArray[i] = 0;	// 数组元素全部赋初值为0
		}
	}

	int& operator [](int i)		// 返回int引用,要符合[]的原生语义
	{				// 既可作为左值,也可作为右值
		return MyArray[i];
	}

	int operator [](int i) const	// const 成员调用
	{
		return MyArray[i];
	}

	int& operator [](const string& s)
	{
		     if( s == "1ST" ){return MyArray[0];}
		else if( s == "2ND" ){return MyArray[1];}
		else if( s == "3RD" ){return MyArray[2];}
		else if( s == "4TH" ){return MyArray[3];}
		else if( s== "5TH" ){return MyArray[4];}
		
		return MyArray[0];	// 默认返回第一个元素
	}


};

int main(int argc,char* argv[])
{
	Array a;
	
	cout << "a[1] = " << a[1] << endl;
	cout << "a[\"2ND\"] = " << a["2ND"] << endl;
	
	a[1] = 1;
	a["2ND"] = 2;

	cout << "a[1] = " << a[1] << endl;
	cout << "a[\"2ND\"] = " << a["2ND"] << endl;
	
	return 0;
}

只要按照操作符的原生语义进行重载,基本都能完成想要的效果。好了,相信认真看代码的你已经能够大概地了解函数重载和操作符重载了。

猜你喜欢

转载自blog.csdn.net/Dream136/article/details/106049072