学到类的继承这边会出现三个容易混淆的概念:重载(overload)、覆盖(override)和隐藏(hide)。
- 重载:同一作用域内的几个函数名字相同但参数列表不同(见C++ Primer 6.4节)。
- 覆盖:派生类对基类中有相同函数签名(指参数列表+返回类型)的函数进行重定义(来自GeeksforGeeks,见C++ Primer 15.2-3节)。
- 隐藏:内作用域的函数会使外作用域的函数变得不可见。
如何厘清这三者之间的关系?给定两个函数,如何判断他们是重载/覆盖/隐藏?看下面这张图就够了。
按照上图的判断过程,可以轻松判断main
函数中每一行代码的输出。
#include <iostream>
using std::cout;
struct Base
{
void f() { cout << "Base f\n"; }
void f(char) { cout << "Base f char\n"; }
void f(int) { cout << "Base f int\n"; }
virtual void g(char) { cout << "Base g char\n"; }
virtual void g(int) { cout << "Base g int\n"; }
virtual void h() { cout << "Base h\n"; }
};
struct Derived : public Base
{
void f() { cout << "Derived f\n"; }
void f(char) { cout << "Derived f char\n"; }
void f(double) { cout << "Derived f double\n"; }
virtual void g() { cout << "Derived g\n"; }
virtual void g(char) { cout << "Derived g char\n"; }
virtual void g(double) { cout << "Derived g double\n"; }
virtual int h() { return 1; } // 如果参数列表相同,必须保证返回类型也相同,才能覆盖;如果返回类型不同,必须保证参数列表也不同,才能隐藏。
//error: conflicting return type specified for 'virtual int Derived::h()'
using Base::f; // 使被基类被隐藏的f变得可见
};
int main()
{
Derived d;
Base *bp = &d;
d.f(); // Derived f
d.f(1); // Base f int
cout << "====\n";
bp->f(); // Base f
bp->f(1); // Base f int
cout << "====\n";
d.g(1.1); // Derived g double
d.g('a'); // Derived g char
d.g(1); // 二义性调用,参数是char还是double? error: virtual void Derived::g(char) or virtual void Derived::g(double)
cout << "====\n";
bp->g(); // 静态类型为基类基类没有名为g的函数不接受参数。error: candidate expects 1 argument, 0 provided
bp->g('a'); // Derived g char
bp->g(1); // Base g int
bp->g(1.1); //静态类型为基类,二义性调用,参数是char还是int? error: virtual void Base::g(char) or virtual void Base::g(int)
cout << "====通过作用域运算符调用另一函数\n";
d.Base::f(); // Base f
bp->Base::g('a'); // Base g char
return 0;
}