C++ 派生类中的函数覆盖和重载

首先,访问类中的成员函数有以下几种方式:

  1. 通过基类和派生类的对象名直接访问
  2. 通过指向基类对象的基类指针和指向派生类对象的派生类指针访问
  3. 通过指向派生类对象的基类指针访问(不存在指向基类的派生类指针)

下面分别总结一下

1. 通过对象名访问

此时和 virtual 关键字没有关系

基类无需多言,派生类 主要看同名函数在派生类中有没有定义

1. 有定义

那么和基类中的同名函数就没有关系了,则使用派生类中的函数(即使需要进行类型转换,如无法转换则编译报错,也不会使用基类中的函数),派生类内部也可以进行重载,甚至可以手动实现一个和基类一模一样的同名函数

2. 没有定义

使用基类中的函数

2. 通过正确类型的指针访问

此时和 virtual 关键字也没有关系此种情形和通过对象名访问无异

3. 通过指向派生类对象的基类指针访问

只有在此种情况下 virtual 关键字才起作用

1. 基类函数有 virtual 关键字

派生类中的同名同参数函数可以对基类函数进行覆盖,对于这些函数则使用派生类中的定义(如果有的话),即使派生类中可能存在其他更符合参数类型的重载,也只使用派生类中和基类参数类型一致的那个覆盖函数

2. 基类函数没有 virtual 关键字

无论派生类中的情况如何都没有关系,因为指向派生类的基类指针只能访问基类的成员函数以及其中带有 virtual 关键字的成员函数在派生类中的实现(覆盖)

注:

  1. virtual 关键字只在通过指向派生类对象的基类指针调用函数时起作用,这是 virtual 关键字唯一的用武之地
  2. virtual 关键字对类本身不会起什么作用,主要是决定了其子类中的同名同参数函数能否对基类函数进行覆盖
  3. 高质量C++/C 编程指南 中指出的类继承中所谓的隐藏规则大致是正确的,只是没有正确理解 virtual 关键字的作用情况,并区分对派生类的调用方式

验证代码:

#include <iostream>

using namespace std;
  
class Base
{
public:
    virtual void f(float x) { cout << "Base::f(float) " << x << endl; }
    virtual void g(float x) { cout << "Base::g(float) " << x << endl; }
    void h(float x) { cout << "Base::h(float) " << x << endl; }
    void j(float x) { cout << "Base::j(float) " << x << endl; }
};

class Derived : public Base
{
public:
    virtual void f(float x) { cout << "Derived::f(float) " << x << endl; }
    virtual void g(float x) { cout << "Derived::g(float) " << x << endl; }
    virtual void g(int x) { cout << "Derived::g(int) " << x << endl; }
    void h(float x) { cout << "Derived::h(float) " << x << endl; }
    void j(int x) { cout << "Derived::j(int) " << x << endl; }
    void k(int x) { cout << "Derived::k(int) " << x << endl; }
};

void main(void)
{
    Base b;
    Derived d;
    Base* pb = &b;
    Derived* pd = &d;
    Base *ppb = &d;

    b.f(3.14f);
    d.f(3.14f);
    pb->f(3.14f); 
    pd->f(3.14f); 
    ppb->f(3.14f);
    cout << endl;

    b.g(3.14f);
    d.g(3.14f);
    pb->g(3.14f);
    pd->g(3.14f);
    ppb->g(3);
    cout << endl;

    b.h(3.14f);
    d.h(3.14f);
    pb->h(3.14f);
    pd->h(3.14f);
    ppb->h(3.14f);
    cout << endl;

    b.j(3.14f);
    d.j(3.14f);
    pb->j(3.14f);
    pd->j(3.14f);
    ppb->j(3.14f);
    cout << endl;

    cin.get();
}