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

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

  • 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;
}
  • 验证结果:

    喵喵喵
    汪汪汪

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

多态原理

  • 那么多态的原理是怎么样的呢?为什么这样写代码,就能实现猫和狗叫声不同,它们明明调用的是同一个接口呀?
  • 我们知道C++在编译时为我们做了很多背后的工作。它为cat和dog分别生成了一张虚函数表,将函数地址(也就是函数指针)记录在各自的虚函数表中。如图所示。
    • cat的虚函数表:
cat的虚函数表
cat的voice函数的地址
- dog的虚函数表:
dog的虚函数表
dog的voice函数的地址
  • 这样当我们用接口调用cat和dog的voice函数时,它们会各自在自己的虚函数表里找到自己的voice函数地址,然后根据这个地址来进行调用了。怎么样,多态实现很简单吧?

虚函数与纯虚函数

  • 那么animal里的纯虚函数virtual void voice()= 0;只能这样写吗?它可以像下面这样写成虚函数吗?

    ```

    virtual void voice()

    {

}

```

  • 答案是可以,但它们是不同的。这里的虚函数是有实现的,只是它的实现方法是空,那么在cat和dog中我们再次实现这个voice就相当于重写(也就是说,我们也可以选择省略(即:不重写)这个voice)。但如果写成纯虚函数,那么我们就必须要在cat和dog中具体实现voice。
  • 也就是这里的虚函数是有这个函数的实现,只是为空;而纯虚函数是这个函数根本还没实现。

原文地址:https://www.cnblogs.com/nonsupport/p/8414358.html

时间: 2024-12-14 06:27:01

多态实现--虚函数与纯虚函数的相关文章

C++ Primer 学习笔记_34_面向对象编程(5)--虚函数与多态(二):纯虚函数、抽象类、虚析构函数、动态创建对象

C++ Primer 学习笔记_34_面向对象编程(5)--虚函数与多态(二):纯虚函数.抽象类.虚析构函数.动态创建对象 一.纯虚函数 1.虚函数是实现多态性的前提 需要在基类中定义共同的接口 接口要定义为虚函数 2.如果基类的接口没办法实现怎么办? 如形状类Shape 解决方法 将这些接口定义为纯虚函数 3.在基类中不能给出有意义的虚函数定义,这时可以把它声明成纯虚函数,把它的定义留给派生类来做 4.定义纯虚函数: class <类名> { virtual <类型> <函

多态,虚函数,纯虚函数

1. 虚函数和纯虚函数可以定义在同一个类(class)中,含有纯虚函数的类被称为抽象类(abstract class),而只含有虚函数的类(class)不能被称为抽象类(abstract class). 2. 虚函数可以被直接使用,也可以被子类(sub class)重载以后以多态的形式调用,而纯虚函数必须在子类(sub class)中实现该函数才可以使用,因为纯虚函数在基类(base class)只有声明而没有定义. 3. 虚函数和纯虚函数都可以在子类(sub class)中被重载,以多态的形式

虚函数和纯虚函数的作用与区别

http://blog.csdn.net/xwpc702/article/details/8670025 虚函数为了重载和多态的需要,在基类中是有定义的,即便定义是空,所以子类中可以重写也可以不写基类中的此函数!纯虚函数在基类中是没有定义的,必须在子类中加以实现,很像java中的接口函数!虚函数引入原因:为了方便使用多态特性,我们常常需要在基类中定义虚函数.class Cman{public:virtual void Eat(){……};void Move();private:};class C

C++ 虚函数与纯虚函数 浅析

[摘要] 本文首先简述虚函数与纯虚函数的定义,然后分析比较两者的区别与联系(DWS). [正文] 1)虚函数与纯虚函数有什么区别? 虚函数,不代表函数为不被实现的函数,为了允许用基类的指针来调用子类的这个函数:允许被其子类重新定义的成员函数. 纯虚函数,才代表函数没有被实现,为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数. 2)虚就虚在所谓"推迟联编"或者"动态联编"上,一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的.

C++中虚函数和纯虚函数的作用与区别-详解

虚函数为了重载和多态的需要,在基类中是有定义的,即便定义是空,所以子类中可以重写也可以不写基类中的此函数! 纯虚函数在基类中是没有定义的,必须在子类中加以实现,很像java中的接口函数! 虚函数 引入原因:为了方便使用多态特性,我们常常需要在基类中定义虚函数. class Cman { public: virtual void Eat(){--}; void Move(); private: }; class CChild : public CMan { public: virtual void

虚函数和纯虚函数

虚函数为了重写和多态的需要,在基类中是有定义的,即便定义是空,所以子类中可以重写也可以不写基类中的此函数! 纯虚函数在基类中是没有定义的,必须在子类中加以实现,很像java中的接口函数! 1.动态绑定 在执行期间(非编译期)判断所引用对象的实际类型,根据实际类型(动态类型)调用相应的方法. 动态绑定灵活性相对静态绑定来说要高,因为它在运行之前可以进行选择性的绑定,但同时,动态绑定的执行效率要低些,因为绑定对象还要进行编译(现在编译期一般都会多次编译). 触发动态绑定的条件:(1)只有指定为虚函数

c++虚函数,纯虚函数

1.虚函数和纯虚函数可以定义在同一个类中,含有纯虚函数的类被称为抽象类,而只含有虚函数的类不能被称为抽象类. 2.虚函数可以被直接使用,也可以被子类重载以后,以多态的形式调用,而纯虚函数必须在子类中实现该函数才可以使用,因为纯虚函数在基类有声明而没有定义. 3.虚函数和纯虚函数都可以在子类中被重载,以多态的形式被调用. 4.虚函数和纯虚函数通常存在于抽象基类之中,被继承的子类重载,目的是提供一个统一的接口. 5.虚函数的定义形式:virtual{};纯虚函数的定义形式:virtual  { }

【C++】C++中的虚函数与纯虚函数

C++中的虚函数 先来看一下实际的场景,就很容易明白为什么要引入虚函数的概念.假设我们有一个基类Base,Base中有一个方法eat:有一个派生类Derived从基类继承来,并且覆盖(Override)了基类的eat:继承表明ISA(“是一个”)的关系,现在我们有一个基类的指针(引用)绑定到派生类对象(因为派生类对象是基类的一个特例,我们当然可以用基类指针指向派生类对象),当我们调用pBase->eat()的时候,我们希望调用的是Derived类的eat,而实际上调用的是Base类的eat,测试

C++ 虚函数 、纯虚函数、接口的实用方法和意义

也许之前我很少写代码,更很少写面向对象的代码,即使有写多半也很容易写回到面向过程的老路上去.在写面向过程的代码的时候,根本不管什么函数重载和覆盖,想到要什么功能就变得法子的换个函数名字,心里想想:反正函数重载本质也就是入栈了两个不同的函数. 回过头来讲,让我了解标题这三个概念的实际用处,还是在于我这第四次重写毕业论文的代码,将它改写成面向对象的时候,才理解的.在面向对象设计的过程中, 类是从抽象逐渐具体起来的,父类可以是非常非常抽象的东西,而最终实例化的子类就非常具体了.在这个继承的过程中,不断