重载、覆盖和隐藏的区别
“overload”翻译过来就是:超载,过载,重载,超出标准负荷;“override”翻译过来是:重置,覆盖,使原来的失去效果。
先来说说重载(Overload)的含义,在日常生活中我们经常要清洗一些东西,比如洗车、洗衣服。尽管我们说话的时候并没有明确地说用洗车的 方式来洗车,或者用洗衣服 的方式来洗一件衣服,但是谁也不会用洗衣服的方式来洗一辆车,否则等洗完时车早就散架了。我们并不要那么明确地指出来就心知肚明,这就有重载的意思了。在 同一可访问区内被声名的几个具有不同参数列的(参数的类型、个数、顺序不同)同名函数,程序会根据不同的参数列来确定具体调用哪个函数,这种机制叫重载, 重载不关心函数的返回值类型。这里,“重载”的“重”的意思不同于“轻重”的“重”,它是“重复”、“重叠”的意思。例如在同一可访问区内有:
① double calculate(double);
② double calculate(double,double);
③ double calculate(double, int);
④ double calculate(int, double);
⑤ double calculate(int);
⑥ float calculate(float);
⑦ float calculate(double);
六个同名函数calculate,①②③④⑤⑥中任两个均构成重载,⑥和⑦也能构成重载,而①和⑦却不能构成重载,因为①和⑦的参数相同。
函数重载:两个函数的形式参数必须不同(比如参数类型、参数个数和参数顺序,这三者中必须至少有一个不同),而函数的返回值类型可以相同也可以不相同,也就是说函数的返回值类型与函数是否发生重载是没有关系的。
覆盖(Override)是指派生类中存在重新定义的函数,其函数名、参数列、返回值类型必须同父类中的相对应被覆盖的函数严格一
致,覆盖函数和被覆盖函数只有函数体
(花括号中的部分)不同,当派生类对象调用子类中该同名函数时会自动调用子类中的覆盖版本,而不是父类中的被覆盖函数版本,这种机制就叫做覆盖。
下面我们从成员函数的角度来讲述重载和覆盖的区别。
成员函数被重载的特征有:
1) 相同的范围(在同一个类中);
2) 函数名字相同;
3) 参数不同;
4) virtual关键字可有可无。
覆盖的特征有:
1) 不同的范围(分别位于派生类与基类);
2) 函数名字相同;
3) 参数相同;
4) 基类函数必须有virtual关键字。
比如,在下面的程序中:
- #include <iostream>
- using namespace std;
- class A
- {
- public:
- void f(int x){cout<<"A::f(int) "<<x<<endl;}
- void f(float x){cout<<"A::f(float) "<< x<<endl;}
- virtual void g(void){cout<< "A::g(void)"<<endl;}
- };
- class B : public A
- {
- public:
- virtual void g(void){cout<<"B::g(void)"<<endl;}
- };
- int main(void)
- {
- B d;
- A *p = &d;
- p->f(42); // 运行结果:A::f(int) 42
- p->f(3.14f); // 运行结果:A::f(float) 3.14
- d.g();// 运行结果:B::g(void)
- p->g(); // 运行结果:B::g(void)
- /*
- 运行结果显示:
- A::f(int) 42
- A::f(float) 3.14
- B::g(void)
- B::g(void)
- 请按任意键继续. . .
- */
- return 0;
- }
函数A::f(int)与A::f(float)相互重载,而A::g(void)被B::g(void)覆盖。
隐藏是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
1) 如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
2) 如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。
比如,在下面的程序中:
- #include <iostream>
- using namespace std;
- class A
- {
- public:
- virtual void f(float x){cout<<"A::f(float) "<<x<<endl;}//虚函数
- void g(float x){cout<<"A::g(float) "<<x<<endl;}
- void h(float x){cout<<"A::h(float) "<<x<<endl;}
- };
- class B : public A
- {
- public:
- virtual void f(float x){cout<<"B::f(float) "<<x<<endl;}//虚函数
- void g(int x){cout<<"B::g(int) "<<x<<endl;}
- void h(float x){cout<<"B::h(float) "<<x<<endl;}
- using A::g;//这句话是用来引用父类中被隐藏的部分的。
- };
- int main(void)
- {
- B test1;
- A *p = &test1;
- test1.g(99);//B::g(int) 99
- test1.h(88.8);//B::h(float) 88.8
- //下面显示了虚函数的功能
- test1.f(66.6);//B::f(float) 66.6
- p->f(55.5);//B::f(float) 55.5
- A test2;
- p = &test2;
- p->f(33.3);//A::f(float) 33.3
- /*
- 输出结果是:
- B::g(int) 99
- B::h(float) 88.8
- B::f(float) 66.6
- B::f(float) 55.5
- A::f(float) 33.3
- 请按任意键继续. . .
- */
- return 0;
- }
通过分析可知:
(1. 函数B::g(int)隐藏了A::g(float),注意,不是重载。
(2. 函数B::h(float)隐藏了A::h(float),注意,不是覆盖。
(3. 函数B::f(float)覆盖了A::f(float),有virtual。
看完前面的示例,可能大家还没明白隐藏与覆盖到底有什么区别,因为我们前面都是讲的表面现象,怎样的实现方式,属于什么情况。下面我们就要分析覆盖与
隐藏在应用中到底有什么不同之处。在下面的程序中bp和dp指向同一地址,按理说运行结果应该是相同的,可事实并非如此。
- <pre class="cpp" name="code">int main(void)
- {
- B b;
- A *p_a = &b;
- B *p_b = &b;
- //good:behavior依赖于对象的类型
- p_a->f(3.14f);
- p_b->f(3.14f);
- //bad:bahavior依赖于指针类型
- p_a->g(3.14f);
- p_b->g(3.14f);
- //bad:behavior依赖于指针类型
- p_a->h(3.14f);
- p_b->h(3.14f);
- /*
- B::f(float) 3.14
- B::f(float) 3.14
- A::g(float) 3.14
- B::g(int) 3
- A::h(float) 3.14
- B::h(float) 3.14
- 请按任意键继续. . .
- */
- return 0;
- }</pre><br>
原文出自【比特网】,转载请保留原文链接:http://soft.chinabyte.com/database/348/12279348.shtml