函数重载

重载是指同一可访问区内被声明几个具有不同参数列(参数的类型、个数、顺序)的同名函数,根据参数列表确定调用哪个函数,重载不关心函数返回类型。

1
2
3
4
5
6
7
8
class A {
public:
void fun(int t);
void fun(float t); // 重载,参数类型不同(相对于上一个函数
void fun(int t, float t1); // 重载,参数个数不同(相对于上一个函数
void fun(float t, int t1); // 重载,参数顺序不同(相当于上一个函数
int fun(int t); // error: 'int A::fun(int)' cannot be overloaded 重载不关心函数返回类型
};

函数隐藏

函数隐藏是指派生类的函数屏蔽了与其同名的基类函数,只要是与基类同名的成员函数,不管参数列表是否相同,基类函数都会被隐藏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std;

class Base {
public:
void fun(int t, float t1) {
cout << "Base::fun(int t, float t1)" << endl;
}
};

class Derived : public Base {
public:
void fun(int t) {
cout << "Derived::fun(int t)" << endl;
}
};

int main() {
Derived ex;
ex.fun(1); // Derived::fun(int t)
ex.fun(1, 0.1); // [Error] no matching function for call to 'Derived::fun(int, double)'
ex.Base::fun(1, 0.1); // Base::fun(int t, float t1)
return 0;
}

上述代码中 ex.fun(1, 0.1); 出现错误,说明派生类中将基类的同名函数隐藏了。若是想调用基类中的同名函数,可以加上类型名指明 ex.Base::fun(1, 0.1); ,这样就可以调用基类中的同名函数。

函数重写(覆盖)

函数覆盖是指派生类中存在重新定义的函数。函数名、参数列表、返回值类型都必须同基类中被重写的函数一致,只有函数体不同。派生类调用时会调用派生类的重写函数,不会调用被重写函数。基类中被重写的函数必须是虚函数,被 virtual 修饰。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
using namespace std;

class Base {
public:
virtual void fun(int t) {
cout << "Base::fun(int t): " << t << endl;
}
};

class Derived : public Base {
public:
virtual void fun(int t) {
cout << "Derived::fun(int t): " << t << endl; // 重写基类中的 fun 函数
}
};

int main() {
Base *p = new Derived();
p->fun(3); // Derived::fun(int t): 3
return 0;
}

重写和重载的区别

  • 范围区别:对于类中函数的重载或者重写而言,重载发生在同一个类的内部,重写发生在不同的类之间(子类和父类之间)。
  • 参数区别:重载的函数需要与原函数有相同的函数名、不同的参数列表,不关注函数的返回类型;重写的函数的函数名、参数列表和返回值类型都需要和原函数相同,父类中被重写的函数需要被 virtual 修饰。
  • virtual 关键字:重写的函数基类中必须有 virtual 关键字的修饰,重载的函数有没有都行。

隐藏和重载、重写的区别

  • 范围区别:隐藏与重载范围不同,隐藏发生在不同类中。
  • 参数区别:隐藏函数和被隐藏函数参数列表可以相同,也可以不同,但函数名一定相同;当参数名不同时,无论基类中的函数是否被 virtual 修饰,基类函数都是隐藏,而不是重写。
  • 利用重写可以实现多态,而隐藏不可以。如果使用基类指针指向派生类对象,利用这个指针调用函数时,对于隐藏的函数,会根据指针的类型区调用函数;对于重写的函数,会根据指针所指向对象的类型去调用函数。重写必须使用 virtual 关键字,此时会更改派生类虚函数表的表项。
  • 隐藏是发生在编译时,在编译时由编译器实现隐藏,而重写一般发生在运行时,即运行时会查找类的虚函数表,决定调用的函数接口。

参考

重载、重写、隐藏的区别

重载、重写(覆盖)和隐藏的区别

C++中重载、重写(覆盖)和隐藏的区别