首先阐述一下各自的定义:
一、重载
1、范围相同(在同一个类里,不能跨类)。
2、函数名字相同。
3、参数不同。
4、virtual 关键字可有可无。
二、覆盖
1、范围不同(派生类与基类)。
2、函数名相同。
3、参数相同。
4、基类必须有 virtual 关键字。
三、隐藏
隐藏分两种情况:
(1)
1、范围不同(派生类与基类)。
2、函数名字相同。
3、参数不同。
4、virtual 关键字可有可无。
(2)
1、范围不同(派生类与基类)。
2、函数名相同。
3、参数相同。
4、基类没有 virtual 关键字。
下面我们看看重载和覆盖的示例:
#include <iostream>
using namespace std;
class CBase
{
public:
void f(int x) { cout<<"CBase::f(int)"<<" "<<x<<endl; }
void f(float x) { cout<<"CBase::f(float)"<<" "<<x<<endl; }
virtual void g(void) { cout<<"CBase::g(void)"<<endl; }
};
class CDerived:public CBase
{
public:
virtual void g(void) { cout<<"CDerived::g(void)"<<endl; }
};
int main(int argc, char* argv[])
{
CDerived d;
CBase *pBase=&d;
pBase->f(10);
pBase->f(10.10f);
pBase->g();
return 0;
}
输出的结果为:
CBase::f(int) 10
CBase::f(int) 10.1
CDerived::g(void)
这说明子类的 g(void) 函数覆盖了父类的 g(void)。
再看看覆盖和隐藏的示例:
#include <iostream>
using namespace std;
class CBase
{
public:
virtual void f(float x) { cout<<"CBase::f(float)"<<" "<<x<<endl; }
void g(float x) { cout<<"CBase::g(float)"<<" "<<x<<endl; }
void h(float x) { cout<<"CBase::h(float)"<<" "<<x<<endl; }
};
class CDerived:public CBase
{
public:
virtual void f(float x) { cout<<"CDerived::f(float)"<<" "<<x<<endl; }
void g(int x) { cout<<"CDerived::g(int)"<<" "<<x<<endl; }
void h(float x) { cout<<"CDerived::h(float)"<<" "<<x<<endl; }
};
int main(int argc, char* argv[])
{
CDerived d;
CBase *pBase=&d;
CDerived *pDerived=&d;
pBase->f(20.20f);
pDerived->f(20.20f);
pBase->g(30.30f);
pDerived->g(30.30f);
pBase->h(40.40f);
pDerived->h(40.40f);
return 0;
}
输出的结果为:
CDerived::f(float) 20.2
CDerived::f(float) 20.2
CBase::g(float) 30.3
CDerived::g(int) 30
CBase::h(float) 40.4
CDerived::h(float) 40.4
上面的例子中 子类 CDerived 的函数 f(float) 覆盖了父类的 f(float)
函数 g 和 f 依赖于指针的类型,如果是子类的指针,则隐藏了父类的函数。
隐藏的麻烦和解决办法:
#include <iostream>
using namespace std;
class CBase
{
public:
void f(int x) { cout<<"CBase::f(int)"<<" "<<x<<endl; }
};
class CDerived:public CBase
{
public:
void f(char *str) { cout<<"CDerived::f(char*)"<<" "<<str<<endl; }、
};
int main(int argc, char* argv[])
{
CDerived *pd=new CDerived;
pd->f(100); // error
return 0;
}
说明:语句 pd->f(100); 的本意是调用 CBase::f(int),但是 CBase::f(int) 不幸被 CDerived::f(char*) 隐藏了。由于数字 100 不能被隐式的转换为字符串,所以编译出错。
隐藏规则至少有两个存在的理由:
(1) 写语句 pd->f(100); 的人可能真的想调用 CDerived::f(char*) 函数,只是他把参数写错了。有了隐藏规则,编译器可以明确的指出错误,这对我们来说是一件好事。如果编译器将错就错,程序员很难发现这个错误,留下祸根。
(2) 假如 CDerived 类有多个基类,有时搞不清哪些基类定义了函数 f。如果没有隐藏规则,那么 pd->f(100) 可能会调用一个出乎意料的基类函数 f。尽管隐藏规则看起来不怎么有道理,但它的确能消灭这些意外。
解决办法: 如果在上面的例子中,一定要用 pd->f(100); 调用CBase::f(int),那么可以这样修改 CDerived 类:
class CDerived:public CBase
{
public:
void f(char *str) { cout<<"CDerived::f(char*)"<<" "<<str<<endl; }
void f(int x) { CBase::f(x); }
};