c++ 函数重载、隐藏、重写(覆盖、虚函数)

函数重载在相同的作用域,同样的函数名,不同的形参(不同顺序、类型、个数)
函数隐藏在不同作用域(基类和派生类),同样的函数名,相同或不同的参数,需要实例化相应的对象才能调用相应的成员函数。
重写、覆盖需要用虚函数,是多态的思想,在不同的作用域,同样的函数名,参数值列表相同,返回值类型相同,只有函数体不同。需要区别隐藏和重写。

1、函数重载

在相同的作用域,同样的函数名,不同的形参(不同顺序、类型、个数)。

根据参数列表确定调用哪个函数,重载不关心返回类型

2、函数隐藏(重定义)

在不同的作用域(基类和派生类),同样的函数名,相同或者不同的参数。

实例化基类对象,可以调用基类的函数

实例化派生类对象,可以调用派生类的函数

派生类可以重写与基类函数名相同的函数,可以修改输入的形参,上述操作叫做函数隐藏

3、重写、覆盖(虚函数)

**在不同的作用域(基类和派生类),同样的函数名,参数列表相同、返回值类型相同,只有函数体不同,**使用关键字virtual(一般就是直接写在基类函数名前,这样派生类的函数就都是虚函数了)。

在进行隐藏后,基类对象可以调用基类函数,派生类对象可以调用派生类函数。但是用基类的指针指向派生类对象,再调用函数时,就只会调用基类的函数,而不会调用派生类对象的派生函数。

但是如果我们加上virtual关键字,指向派生类对象的基类指针就可以调用其指向的派生类对象的函数。

将派生类对象赋值给基类的指针或者引用来调用派生类的方法有很多好处,例如可以利用基类对象动态确定其所指向的派生类对象的函数方法,就会避免我们写很多函数,一一调用。

当我们加上virtual,基类指针或者引用就可以动态确定派生类对象的方法,并调用,而不是调用基类的方法了。

**隐藏和重写的一个区别:
**在使用效果上,重写可以用于实现多态,隐藏不可以,即使都使用了虚函数。如果使用基类指针p指向派生类对象,利用这个指针调用函数时,对于隐藏的函数,会根据指针的类型去调用函数;对于重写的函数,会根据指针所指对象的类型去调用函数。可能是因为只有重写才会更改派生类虚函数表的表项。

补充:

override关键字的作用

如果派生类在虚函数声明时候使用了override,那么该函数必须重写其基类中的同名函数,否则无法通过编译。

可以利用这一性质检查虚函数是否真正重写正确了,否则编译可以通过,但是没有实现真正的重写。override是让编译器检查你是否重写虚函数啦,没有的话 编译阶段提示一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct Base 
{
virtual void Turing() = 0;
virtual void Dijkstra() = 0;
virtual void VNeumann(int g) = 0;
virtual void DKnuth() const;
void Print();
};
struct DerivedMid: public Base
{
// void VNeumann(double g);
//接口被隔离了,曾想多一个版本的VNeumann函数
};
struct DerivedTop : public DerivedMid
{
void Turing() override;
void Dikjstra() override; //无法通过编译,拼写错误,并非重载
void VNeumann(double g) override; //无法通过编译,参数不一致,并非重载
void DKnuth() override; //无法通过编译,常量性不一致,并非重载
void Print() override; //无法通过编译,非虚函数重载
};

const修饰的常成员函数也可以重载

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

class Base
{
public:
void fun(int tmp, float tmp1) { cout << "Base::fun(int tmp, float tmp1)" << endl; }
void fun(int tmp, float tmp1) const { cout << "const Class !!!" << endl; }
};

int main(){
Base b;
b.fun(1,2.0); //Base::fun(int tmp, float tmp1)

const Base cb;
cb.fun(1,2.5); //const Class !!!
return 0;

原因是const修饰可以理解为, 这里有一个常量指针,即this指向的地址里的内容不可以改变。

1
2
void fun (const Base *this, int tmp, float tmp1);

无const修饰可以理解为:

1
void fun (Base *this, int tmp, float tmp1);