函数重载
出现在相同作用域中的两个函数,如果具有相同的名字而形参表不同,则称为重载函数。
作用:省去了为函数起名并记住函数名字的麻烦,简化了程序的实现,使程序更容易理解。
注意:main函数不能重载;函数不能仅仅基于不同的返回值类型而实现重载;进行函数重载时,要求同名函数在参数个数上不同,或者参数类型上不同;局部声明的函数将屏蔽而不是重载在外层作用域中声明的同名函数。
int add(int, int);
double add(double, double);
重载确定的三个步骤:
1、候选函数
确定该调用所考虑的重载函数集合。
2、选择可行函数
可行函数必须满足的条件:函数的形参个数与该调用的实参个数相同;每一个实参的类型必须与对应形参的类型匹配,或者 可被隐式转换为对应的形参类型。
3、寻找最佳匹配
实参类型与形参类型越接近则匹配越佳。
为了确定最佳匹配,编译器将实参类型到相应形参类型的转换划分等级,降序排列如下:
1)精确匹配(exact match)。实参与形参类型相同。
2)通过类型提升(promotion)实现的匹配。
3)通过标准转换(standart conversion)实现的匹配。
4)通过类类型转换(class-type conversion)实现的匹配。
注意:无法将整型值传递给枚举类型的形参,但可以将枚举值传递给整型形参,此时,枚举值被提升为int型或更大的整型。
enum ID{ MOVE = 100, STOP = 101};
void fun(ID);
void fun(int);
int main()
{
fun(100);//调用fun(int)
fun(MOVE);//调用fun(ID),如果没有fun(ID),则会调用fun(int)
return 0;
}
仅当形参是引用或指针时,可基于函数形参是指向const对象还是指向非const对象,实现函数重载,但是不能基于指针本身是否为const来实现函数的重载。
fun(int *);
fun(int * const);//重复声明
附:运算符重载——
1、除了. .* :: ?: sizeof typeid这几个运算符不能被重载,其他运算符都能被重载。
2、重载不能改变该运算符用于内置类型时的含义,程序员不能改变运算符+用于两个int型时的含义。
3、运算符函数的参数至少有一个必须是类的对象或者类的对象的引用。这种规定可以防止程序员运用运算符改变内置类型的含义。
4、重载不能改变运算符的优先级。
5、重载不能改变运算符的结合律。
6、重载不能改变运算符操作数的个数。比如+需要两个操作数,则重载的+也必须要有两个操作数。
函数隐藏
每个类都保持着自己的作用域,在该作用域中定义了成员的名字。在继承情况下,派生类的作用域嵌套在基类作用域中。如果不能再派生类作用域中确定名字,就在外围基类作用域中查找该名字的定义。
与基类成员同名的派生类成员将屏蔽对基类成员的直接访问,但可以使用域操作符访问被屏蔽成员。
在基类和派生类中使用同一名字的成员函数,其行为与数据成员一样:在派生类作用域中派生类成员将屏蔽基类成员。规则如下:
1) 如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
2) 如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。
class Base
{
public:
int Add(int a,int b){return a+b;}
int mem;
};
class Derived : public Base
{
public:
string Add(string a,string b){return a+b;}
int mem;
};
int main()
{
Base B;
Derived D;
Base *pB = &D;
cout<<"B.Add(1,2)结果为:"<<B.Add(1,2)<<endl; //调用Derived::Add(int a,int b)成功;
cout<<"pB->Add(1,2)结果为:"<<pB->Add(1,2)<<endl; //调用Derived::Add(int a,int b)成功;
cout<<"D.Add(hello ,world)结果为:"<<D.Add("hello ","world")<<endl; //调用Derived::Add(string a,string b)成功;
//B.Add("hello ","world"); //调用Base::Add(int a, int b)出错;
//pB->Add("hello ","world");//调用Base::Add(int a, int b)出错;
//D.Add(1,2); //调用Derived::Add(string a,string b)出错;
cin.get();
return 0;
}
函数覆盖
函数覆盖发生在父类与子类之间,其函数名、参数类型、返回值类型必须同父类中的相对应被覆盖的函数严格一致,覆盖函数和被覆盖函数只有函数体不同,当派生类对象调用子类中该同名函数时会自动调用子类中的覆盖版本,而不是父类中的被覆盖函数版本,这种机制就叫做函数覆盖。
覆盖的特征有:
1) 不同的范围(分别位于派生类与基类);
2) 函数名字相同;
3) 参数相同;
4) 基类函数必须有virtual关键字。
#include <iostream>
using namespace std;
class Base
{
public:
void f(int x){ cout<<"Base::f(int) "<<x<<endl; }
void f(float x){ cout<<"Base::f(float) "<<x<<endl; }
virtual void g(void){ cout<<"Base::g(void)"<<endl;}
};
class Derived : public Base
{
public:
virtual void g(void){ cout<<"Derived::g(void)"<<endl;}
};
int main(void)
{
Derived d;
Base *pb = &d;
pb->f(42); // 运行结果: Base::f(int) 42
pb->f(3.14f); // 运行结果: Base::f(float) 3.14
pb->g(); // 运行结果: Derived::g(void)
cin.get();
return 0;
}
函数Base::f(int)与Base::f(float)相互重载,而Base::g(void)被Derived::g(void)覆盖。
参考:C++ Primer 第四版