c++语言中虚函数实现多态的原理详解

 更新时间:2019年05月28日 08:26:12   作者:coding小菜鸟  
这篇文章主要给大家介绍了关于c++语言中虚函数实现多态的原理的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用c++语言具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

前言

自上一个帖子之间跳过了一篇总结性的帖子,之后再发,今天主要研究了c++语言当中虚函数对多态的实现,感叹于c++设计者的精妙绝伦

c++中虚函数表的作用主要是实现了多态的机制。首先先解释一下多态的概念,多态是c++的特点之一,关于多态,简而言之就是 用父类的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数,这种方法呢,可以让父类的指针具有多种形态,也就是说不需要改动很多的代码就可以让父类这一种指针,干一些很多子类指针的事情,这里是从虚函数的实现机制层面进行研究

在写这篇帖子之前对于相关的文章进行了查阅,基本上是大段的文字,所以我的这一篇可能会用大量的图形进行赘述(如果理解有误的地方,烦请大佬能够指出),接下来就言归正传:

首先介绍一下为什么会引进多态呢,基于c++的复用性和拓展性而言,同类的程序模块进行大量重复,是一件无法容忍的事情,比如我设置了苹果,香蕉,西瓜类,现在想把这些东西都装到碗这个函数里,那么在主函数当中,声明对象是必须的,但是每一次装进碗里对于水果来说,都要用自己的指针调用一次装的功能,那为什么不把这些类抽象成一个水果类呢,直接定义一个水果类的指针一次性调用所有水果装的功能呢,这个就是利用父类指针去调用子类成员,但是这个思想受到了指针指向类型的限制,也就是说表面指针指向了子类成员,但实际上还是只能调用子类成员里的父类成员,这样的思想就变的毫无意义了,如果想要解决这个问题,只要在父类前加上virtual就可以解决了,这里就是利用虚函数实现多态的实例。

首先还是作为举例来两个类,在之前基础知识的帖子中提到过,空类的大小是一个字节(占位符),函数,静态变量都在编译期就形成了,不用类去分配空间,但是做一个小实验,看一看在定义了虚函数之后,类的大小是多少呢

#include<iostream>
using namespace std;
class CFather 
{
public:
  virtual void AA()  //虚函数标识符
  {
    cout << "CFather :: AA()" << endl;
  }
  void BB()
  {
    cout << "CFather :: BB()" << endl;
  }
};
class CSon : public CFather
{
public:
  void AA()
  {
    cout << "CSon :: AA()" << endl;
  }
  void BB()
  {
    cout << "CSon :: BB()" << endl;
  }
};
int main()
{
  cout << sizeof(CFather) << endl;         //测试加了虚函数的类

  system("pause");
  return 0;
}

很明显类里装了一个 4个字节的东西,除了整形int,就是指针了,没错这里装的就是函数指针

先把这个代码,给抽象成图形进行理解,在这CFather为A,CSon为B

此时就是一个单纯的继承的情况,不存在虚函数,然后我new一个对象,A *p = new A;那么 p -> AA(),必然是指向A类中的AA()函数,那么函数的调用有两种方式 一种函数名加()直接调用,一种是利用函数指针进行调用,在这里我想要调用子类的,就可以利用函数指针进行调用,假设出来两个函数指针,来指向B类中的两个成员函数,如果我父类想要调用子类成员,就可以通过 p指针去调用函数指针,再通过函数指针去调用成员函数

每一个函数都可以用一个函数指针去指着,那么每一类中的函数指针都可以形成自己的一个表,这个就叫做虚函数表

那么在创建对象后,为什么类中会有四个字节的内存空间呢?

在C++的标准规格说明书中说到,编译器必需要保证虚函数表的指针存在于对象中最前面的位置(这是为了保证正确取到虚函数的偏移量)。这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。也就是说这四个字节的指针,代替了上图中(p->*pfn)()的作用,指向了函数指针,也就是说,在使用了虚函数的父类成员函数,虽然写的还是p->AA(),实际上却是,(p->*(vfptr[0])),而指向哪个虚函数表就由,创建的对象来决定

至此,就能理解如何用虚函数这个机制来实现多态的了

下面,我将分别说明“无覆盖”和“有覆盖”时的虚函数表的样子。没有覆盖父类的虚函数是毫无意义的。我之所以要讲述没有覆盖的情况,主要目的是为了给一个对比。在比较之下,我们可以更加清楚地知道其内部的具体实现。

无虚数覆盖

下面,再让我们来看看继承时的虚函数表是什么样的。假设有如下所示的一个继承关系:

请注意,在这个继承关系中,子类没有重载任何父类的函数。那么,在派生类的实例中,Derive d; 的虚函表:

我们可以看到下面几点:

1)虚函数按照其声明顺序放于表中。

2)父类的虚函数在子类的虚函数前面。

有虚数覆盖

覆盖父类的虚函数是很显然的事情,不然,虚函数就变得毫无意义。下面,我们来看一下,如果子类中有虚函数重载了父类的虚函数,会是一个什么样子?假设,我们有下面这样的一个继承关系。

为了让大家看到被继承过后的效果,在这个类的设计中,我只覆盖了父类的一个函数:f()。那么,对于派生类的实例,其虚函数表会是下面的一个样子:

我们从表中可以看到下面几点,

1)覆盖的f()函数被放到了虚表中原来父类虚函数的位置。

2)没有被覆盖的函数依旧。

这样,我们就可以看到对于下面这样的程序,

Base *b = new Derive();

b->f();

由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代,于是在实际调用发生时,是Derive::f()被调用了。这就实现了多态。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。

相关文章

  • linux c 查找使用库的cflags与libs的方法详解

    linux c 查找使用库的cflags与libs的方法详解

    本篇文章是对在linux中使用c语言查找使用库的cflags与libs的方法进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • CMake语法及CMakeList.txt简单使用小结

    CMake语法及CMakeList.txt简单使用小结

    Cmake主要用于开发跨平台的C++项目,本文主要介绍了CMake语法及CMakeList.txt简单使用小结,具有一定的参考价值,感兴趣的可以了解一下
    2022-05-05
  • VsCode安装和配置c/c++环境小白教程(图文)

    VsCode安装和配置c/c++环境小白教程(图文)

    本文主要介绍了VsCode安装和配置c/c++环境小白教程,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • C++内存泄漏的检测与实现详细流程

    C++内存泄漏的检测与实现详细流程

    内存泄漏(memory leak) 是指由于疏忽或错误造成了程序未能释放掉不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费
    2022-08-08
  • 关于C++中的static关键字的总结

    关于C++中的static关键字的总结

    C++的static有两种用法:面向过程程序设计中的static和面向对象程序设计中的static。前者应用于普通变量和函数,不涉及类;后者主要说明static在类中的作用
    2013-09-09
  • C++面向对象语言自制多级菜单功能实现代码

    C++面向对象语言自制多级菜单功能实现代码

    菜单类主要负责菜单的创建、修改、删除,是包含菜单结构组织和响应函数的模型,用户拥有充分的自主性,可根据需要自定义菜单显示和响应函数,这篇文章主要介绍了C++面向对象语言自制多级菜单,需要的朋友可以参考下
    2024-06-06
  • C++ xxx_cast实现转换代码实例解析

    C++ xxx_cast实现转换代码实例解析

    这篇文章主要介绍了C++xxx_cast转换代码实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-07-07
  • C++ opencv图像处理实现图片几何变换示例

    C++ opencv图像处理实现图片几何变换示例

    这篇文章主要为大家介绍了C++ opencv图像处理实现图片几何变换示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • C++ 读取文件内容到指定类型的变量方法

    C++ 读取文件内容到指定类型的变量方法

    今天小编就为大家分享一篇C++ 读取文件内容到指定类型的变量方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • c++智能指针的超详细讲解

    c++智能指针的超详细讲解

    c++程序设计中经常会用堆内存,程序员要自己管理内存的申请和释放,使用原始指针,容易造成堆内存泄漏(忘记释放),二次释放,使用智能指针能更好的管理堆内存,下面这篇文章主要给大家介绍了关于c++智能指针的相关资料,需要的朋友可以参考下
    2022-06-06

最新评论