C++对C语言进行了巨大的扩展,其中重载就是其中之一,重载概念的由来是面向对象编程的思想延申,把现实生活中的东西融入到编程语言。重载相当于自然语言中的词汇搭配,打球,打篮球,打羽毛球,打乒乓球,打字与不同的名词结合产生不同意思,重载使得C++拥有丰富的语义表达能力。要时刻知道的是,函数重载的本质是不同的函数,C++编译器在背后做了很多的事情。现在我们就先来逐一解析函数重载吧。
#include
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
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
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
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
using namespace std;
string s1 = "hello";
string s2 = "world";
string s3 = s1 + s2; // s3 = "helloworld";
能够重载的操作符只能是语言系统预定义的操作符,最常用的+ - * /,这些操作符可以重载为全局函数,如果此时操作符重载函数的参数是类对象,一般而言,都需要将该全局函数声明为类的友元(friend),为了简便,通常将操作符重载函数作为类的成员函数来实现,并且还可以减少一个参数(左操作数),还不用声明为友元,一举两得,快哉快哉。先来个全局重载试试水。
#include
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
#include
#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;
}
只要按照操作符的原生语义进行重载,基本都能完成想要的效果。好了,相信认真看代码的你已经能够大概地了解函数重载和操作符重载了。