C++入门--函数覆盖

  • 函数重载
  1. 函数名相同
  2. 参数列表(个数/类型/顺序)不同
  3. 相同作用域
  4. 函数重载不考虑返回值的不同
  • 函数隐藏
  1. 作用域不同
  2. 函数名相同
  3. 参数和返回值不考虑
  • 函数覆盖(虚函数)
  1. 作用域不同(父子类之间的继承关系)
  2. 函数名,参数列表(参数个数/顺序/类型),返回值,调用约定必须相同
  3. 有virtual关键字

看一个例子:

 1 #include <stdlib.h>
 2 
 3 #include <iostream>
 4 #include <string>
 5 using namespace std;
 6 
 7 class CPerson {
 8    public:
 9     void speak() { cout << "speak" << endl; }
10 };
11 
12 class CChinese : public CPerson {
13    public:
14     virtual void speak() { cout << "speak Chinese" << endl; }
15 };
16 
17 int main(int argc, char const* argv[]) {
18 
19     CPerson per;
20     int nPersonSize = sizeof(CPerson);
21     cout << nPersonSize << endl;
22 
23     return 0;
24 }

CPerson类仅有一个非虚函数的speak方法时,获取CPerson的大小

nPersonSize = 1;   //1字节为一个占位符

将CPerson的speak声明为virtual虚函数

virtual void speak() { cout << "speak" << endl; }

此时CPerson的大小:

nPersonSize = 4;  //虽然没有成员变量,但是有virtual关键字,有4字节的虚表指针

二、虚表中虚函数的顺序:

1.子类继承了所有父类虚函数(公有)

2.父类的虚函数顺序决定了子类虚函数的顺序
3.子类重写了父类的某虚函数,则会在子类自己的虚表中覆盖对应位置的函数
4.子类未重写某虚函数,则直接继承父类的该虚函数
5.子类自己的虚函数会出现在前面父类所有虚函数后面
 

三、虚函数的直接调用与间接调用

1、直接调用

      根据函数名称,直接调用该函数(编译器在编译时候就确定了其跳转地址)

  1)普通函数的调用

  2)对象的普通成员函数的调用

  3)对象的虚函数的调用

2、间接调用(虚调用,通过查虚表来调用)

  虚函数,通过查找对象的虚表下标来调用函数的方法(在运行时期确定调用谁)

  1)通过对象的指针调用虚函数        

CChinese* pChs = &chs;
pChs->foo();

  2)通过对象的引用调用虚函数

CChinese&  pChs = &chs;
pChs->foo();

  3)以上两情况下,若明确了类域范围,则为直接调用

pChs->CChinese::foo();

四、函数覆盖隐藏重载

看以下示例,判断main输出结果

#include <stdlib.h>

#include <iostream>
#include <string>
using namespace std;

class Base {
   public:
    virtual void Handle1(float x) { cout << "Base::Handle1(float)" << endl; }
    void Handle2(float x) { cout << "Base::Handle2(float)" << endl; }
    void Handle3(float x) { cout << "Base::Handle3(float)" << endl; }
};

class Derived : public Base {
   public:
    virtual void Handle1(float x) { cout << "Derived::Handle1(float)" << endl; }
    void Handle2(int x) { cout << "Derived::Handle2(int)" << endl; }
    void Handle3(float x) { cout << "Derived::Handle3(float)" << endl; }
    void Handle3(double x) { cout << "Derived::Handle3(double)" << endl; }
};

int main(int argc, char const *argv[]) {
    Derived DervObj;   //定义了一个子类的对象
    //把子类对象传递给了父类,得到父类指针,子类指针强转父类指针是安全的
    Base *pBase = &DervObj;   
    Derived *pDerv = &DervObj;  //得到子类指针

    pBase->Handle1(3.14f);
    pDerv->Handle1(3.14f);

    pBase->Handle2(3.14f);
    pDerv->Handle2(3.14f);

    pBase->Handle3(3.14f);
    pDerv->Handle3(3.14f);
    pDerv->Handle3(3.14);

    return 0;
}
分析以下:
  • 子类的1与父类的handle1,作用域不同(父子继承关系) ,函数名参数返回值都相同,且为虚函数,故为函数覆盖
  • 看父子的Handle2、3,作用域不同,函数名相同,参数不管,为函数隐藏
  • 看子类的Handle3,作用域相同,函数名相同,参数不同,为函数重载
// 间接调用:查表(查找子类的虚表),子类对象有函数覆盖,调用子类的Handle1
 pBase->Handle1(3.14f);
//间接调用:查表(查找子类的虚表),子类对象有函数覆盖,调用子类的Handle1
 pDerv->Handle1(3.14f);

原文地址:https://www.cnblogs.com/y4247464/p/13819881.html