关于虚函数实现多态的原理及分析

 更新时间:2023年02月05日 16:37:38   作者:Cynantrs  
这篇文章主要介绍了C++中如何实现多态问题,具有很好的参考价值,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

1、C++中如何实现多态

  • 基类中先声明一个虚函数
  • 至少有一个继承该基类的子类

2、虚函数实现多态的原理

  • 当一个类中出现虚函数或着子类继承了虚函数时,就会在该类中产生一个虚函数表(virtual table),虚函数表实际上是一个函数指针数组(在有的编译器作用下是链表),里面的每一个元素对应指向该类中的某一个虚函数的指针。
  • 被该类声明的对象会包含一个虚函数表指针(virtual table pointer),指向该类的虚函数表的地址。
  • 虚函数的调用过程: 当一个对象要调用到虚函数时,先将对象内存中的vptr指针(虚函数表指针)指向定义该类的vtbl(虚函数表),vtbl再寻找里面的指针指向想要调用的虚函数,从而完成虚函数的调用。

2.1 单类继承

定义一个父类

class Person{
public:
	virtual void f(){cout << "use f()" << endl;}
	virtual void g(){cout << "use g()" << endl;}
	virtual void h(){cout << "use h()" << endl;}
};

父类对象其在内存中布局如下:

  • 再定义一个子类,此时并不覆盖父类的虚函数:
class Bag:public Person{
public:
	virtual void i(){cout << "use i()" << endl;}
	virtual void j(){cout << "use j()" << endl;}
};

可以看出虚函数表内的虚函数是按声明顺序进行排序的

父类虚函数排在子类虚函数之前

  • 当我们把子类中的虚函数覆盖掉:(修改Bag类)
class Bag:public Person{
public:
	void f(){cout << "class Bag use fun" << endl;}
	virtual void i(){cout << "use i()" << endl;}
	virtual void j(){cout << "use j()" << endl;}
}; 

子类覆盖的虚函数,放在父类原先放该虚函数的位置上。

所以当父类指针指向该子类对象时,会调用该子类的重载函数

2.2 多类继承

  • 子类没有覆盖父类的虚函数

子类的虚函数放在第一张虚函数表中,紧跟着第一个父类的虚函数

如果每个父类都有虚函数,则有几个父类就有几张虚函数表

  • 子类覆盖父类的虚函数

父类的虚函数被子类覆盖后,则该子类对应的重载函数的位置在被覆盖的父类函数的位置上。(如果父类没有该虚函数,则不用被覆盖)

父类的虚函数被子类覆盖后,则父类指针指向该子类对象时,调用的f()便是子类中重载的f()

示例

#include <iostream>
using namespace std;

class Person1{
public:
    virtual void f(){}
    virtual void g(){}
    virtual void h(){}
    virtual ~Person1(){}
};

class Person2{
public:
    virtual void f(){}
    virtual void g(){}
    virtual void h(){}
    void a(){           // 成员函数,不需要重载
        cout << "class Person2" << endl;
    }
    virtual ~Person2(){}
};

class Person3{
public:
    virtual void g(){}
    virtual void h(){}
    virtual ~Person3(){}
};

class Bag:public Person1, public Person2, public Person3{
public:
    void f(){
        cout << "Bag f()" << endl;
    }
    void g(){
        cout << "Bag g()" << endl;
    }
    void a(){
        cout << "Class Bag" << endl;
    }
};

int main(int argc, char const *argv[])
{
    Person3* p3 = new Bag;
    //p3->f();    // P3 没有成员函数f()
                  // 多态首先得是 父类有虚函数,其次是子类要定义该函数的重载
                  // 如果父类的虚函数改为成员函数,则子类无法进行重载,即无法实现多态
    delete p3;
    p3 = NULL;

    Person1* p1 = new Bag;
    p1->f();
    delete p1;
    p1 = NULL;

    Person2* p2 = new Bag;
    p2->a();
    delete p2;
    p2 = NULL;
    
    return 0;
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • C和C++混合编程问题

    C和C++混合编程问题

    这篇文章主要介绍了C和C++混合编程问题,需要的朋友可以参考下
    2015-10-10
  • C++ 自增、自减运算符的重载和性能分析小结

    C++ 自增、自减运算符的重载和性能分析小结

    这篇文章主要介绍了C++ 自增、自减运算符的重载和性能分析小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • C++函数三种传参形式(指针传递、引用传递、值传递)

    C++函数三种传参形式(指针传递、引用传递、值传递)

    不论是哪种参数传递方式,都有形参和实参之分,本文主要介绍了C++函数三种传参形式(指针传递、引用传递、值传递),具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • C++操作.json文件的超详细新手教程

    C++操作.json文件的超详细新手教程

    最近因为项目原因需要解析JSON格式数据,所以这篇文章主要给大家介绍了关于C++操作.json文件的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-08-08
  • C语言用fstat函数获取文件的大小方法

    C语言用fstat函数获取文件的大小方法

    今天小编就为大家分享一篇关于C语言用fstat函数获取文件的大小方法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • 基于C++编写一个键盘提示音程序

    基于C++编写一个键盘提示音程序

    首先讲一下思路,这次制作的小黑子相当于键盘提示音,输入J,N,T,M,会发出“鸡你太美”的声音,连续按下JNTM则会发出“你干嘛啊,哎呦”的声音,感兴趣的可以了解一下
    2023-03-03
  • 详解state状态模式及在C++设计模式编程中的使用实例

    详解state状态模式及在C++设计模式编程中的使用实例

    这篇文章主要介绍了state状态模式及在C++设计模式编程中的使用实例,在设计模式中策略用来处理算法变化,而状态则是透明地处理状态变化,需要的朋友可以参考下
    2016-03-03
  • Qt自绘实现苹果按钮滑动效果的示例代码

    Qt自绘实现苹果按钮滑动效果的示例代码

    这篇文章主要介绍了Qt自绘实现苹果按钮滑动效果的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • C语言进阶数据的存储机制完整版

    C语言进阶数据的存储机制完整版

    这篇文章主要为大家完整的介绍了C语言进阶数据的存储机制,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-02-02
  • C++11中std::function与std::bind的用法实例

    C++11中std::function与std::bind的用法实例

    大家都知道C++11中增加了许多的新特性,下面这篇文章主要给大家介绍了关于C++11中std::function与std::bind的用法,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-05-05

最新评论