• 多态实现--虚函数与纯虚函数


    多态实现--虚函数与纯虚函数

    • C++中实现多态是使用虚函数表的方法实现的。
    • 那么具体怎么实现的呢?

    举例说明

    • 假设有这样一个多态场景:
    • 有一个基类动物(animal类),动物里面又有两个派生类:猫(cat类)和狗(dog类)。现在要求动物类有一个共同的方法:叫声(voice成员函数),但猫和狗叫声是不同的(即:它们的叫声实现方法不同)。
    • 那么代码怎么写呢?

    多态的代码实现

    #include <iostream>
    using namespace std;
    
    //1、 定义一个带纯虚函数的抽象类作为基类
    class animal
    {
    public:
        virtual void voice()= 0;    //纯虚函数voice
    };
    
    //2、 定义猫(cat)狗(dog)类,共同继承自animal,但对voice进行了具体实现
    class cat:public animal
    {
    public:
        virtual void voice()
        {
            cout <<"喵喵喵"<<endl;
        }
    };
    
    class dog:public animal
    {
    public:
        virtual void voice()
        {
            cout <<"汪汪汪"<<endl;
        }
    };
    
    //3、那么下一步就要给猫和狗做一个统一接口了,传参用基类指针。这个接口只有一个功能,就是调用动物叫声voice。
    void animal_voice(animal * a)
    {
        a->voice();
    }
    
    //4、 多态写好了,我们来调用一下试试,写个test看看这个多态有没有问题。我们分别定义一个猫狗对象,来调用统一接口,看看它们的叫声是否相同。
    
    void test()
    {
        cat c;
        dog d;
        animal_voice(&c);
        animal_voice(&d);
    }
    
    int main()
    {
        test();
        return 0;
    }
    
    • 验证结果:
    喵喵喵
    汪汪汪
    

    说明这个多态已实现完成。

    多态原理

    • 那么多态的原理是怎么样的呢?为什么这样写代码,就能实现猫和狗叫声不同,它们明明调用的是同一个接口呀?

    • 实际上这个例子的程序运行时,为cat和dog分别生成了一张虚函数表,将函数地址记录在各自的虚函数表中。如图所示。

      • cat的虚函数表:
      cat的虚函数表
      cat的voice函数的地址
      • dog的虚函数表:
      dog的虚函数表
      dog的voice函数的地址
    • 这样当我们用接口调用cat和dog的voice函数时,它们会各自在自己的虚函数表里寻找,找到自己的voice函数地址,根据这个地址来进行调用。怎么样,多态实现原理也很简单吧?

    虚函数与纯虚函数

    • 那么animal里的纯虚函数“virtual void voice()= 0;”只能这样写吗?它可以像下面这样写成虚函数吗?
    virtual void voice()
    {
    
    }
    
    • 答案是可以,但它们是不同的。写成虚函数,说明是有实现的(animal也因此变得不再是抽象类),只是它的实现方法是空,那么在cat和dog中我们再次实现这个voice时,就自动将基类的该voice方法重写(override)了(也就是说,我们也可以选择省略(即:不override)这个voice)。但如果写成纯虚函数,那么我们就必须要在cat和dog中具体实现voice,才能使用voice,否则cat和dog也和animal一样是抽象类,不能用来实例化对象。

    • 也就是虚函数是有这个函数的实现,而纯虚函数是这个函数根本还没实现。

    • 再补一个小知识:如果基类的voice写成虚函数,而不是纯虚函数话,派生类即使将其override了,还是可以通过作用域的方法来访问的(实际上override时,基类的voice方法只是被默认隐藏了)。

  • 相关阅读:
    《孙子兵法》【行军第九】
    《孙子兵法》【虚实第六】
    《孙子兵法》【地形第十】
    企业无线局域网的搭建
    企业无线局域网的搭建
    UDDI
    (转载)Linux:Ldd命令介绍及使用方法
    (转载)传递给const引用形参的实参要求
    (转载)千万不要把bool设计成函数参数
    (转载)Linux下如何修改终端提示符?
  • 原文地址:https://www.cnblogs.com/linkyip/p/8414358.html
Copyright © 2020-2023  润新知