• 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);

  • 相关阅读:
    parser_url
    fsockopen
    MySql支持的数据类型
    MySql常用字符集
    MySQL各大存储引擎
    MySql数据库基础
    Python 流程控制 超全解析(不可错过)
    python 序列解包(解压缩)
    python常量 (最全常量解析)
    python内存管理(通俗易懂,详细可靠)
  • 原文地址:https://www.cnblogs.com/y4247464/p/13819881.html
Copyright © 2020-2023  润新知