c++虚函数在g++中的实现方法

  介绍

这篇文章主要介绍c++虚函数在g++中的实现方法,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

探索c++虚函数在g++中的实现

在开始之前,原谅我先借用一张图黑一下c++:

"无敌”的c++

如果你也在写c++,请一定小心…至少,你要先有所了解:<强>当你在写虚函数的时候,g++在写什么?

先写个例子

为了探索c++虚函数的实现,我们首先编写几个用来测试的类,代码如下:c++

 # include & lt; iostream>
  
  使用名称空间性病;
  
  类Base1
  {
  公众:
  虚拟空间f () {
  cout & lt; & lt;“Base1: f (),& lt; & lt;endl;
  }
  };
  
  类Base2
  {
  公众:
  虚拟空间g () {
  cout & lt; & lt;“Base2: g ()“;& lt; & lt;endl;
  }
  };
  
  派生类:公共Base1公共Base2
  {
  公众:
  虚拟空间f () {
  cout & lt; & lt;“派生:f (),& lt; & lt;endl;
  }
  
  虚拟空间g () {
  cout & lt; & lt;“派生:g ()“;& lt; & lt;endl;
  }
  
  虚拟空间h () {
  cout & lt; & lt;“派生:h ()“;& lt; & lt;endl;
  }
  };
  
  int主要(int命令行参数个数,char * argv [])
  {
  派生的ins;
  Base1, b1=ins;
  Base2, b2=ins;
  派生和d=ins;
  
  b1.f ();
  b2.g ();
  d.f ();
  d.g ();
  d.h ();
  }

代码采用了多继承,是为了更多的分析出g++的实现本质,用UML简单的画一下继承关系:

 c++虚函数在g++中的实现方法

示例代码UML图

代码的输出结果和预期的一致,c++实现了虚函数覆盖功能,代码输出如下:

派生::f ()   派生:g ()   派生:f ()   派生:g ()   派生:h ()

开始分析!

我写这篇文章的重点是尝试解释g++编译在底层是如何实现虚函数覆盖和动态绑定的,因此我假定你已经明白基本的虚函数概念以及虚函数表(vtbl)和虚函数表指针(vptr)的概念和在继承实现中所承担的作用,如果你还不清楚这些概念,建议你在继续阅读下面的分析前先补习一下相关知识,陈皓的《c++虚函数表解析》系列是一个不错的选择。

通过本文,我将尝试解答下面这三个问题:

<李>

g++如何实现虚函数的动态绑定?

<李>

vtbl在何时被创建? vptr又是在何时被初始化?

<李>

在Linux中运行的c++程序虚拟存储器中,vptr, vtbl存放在虚拟存储的什么位置?

首先是第一个问题:

g++如何实现虚函数的动态绑定?

这个问题乍看简单,大家都知道是通过vptr和vtbl实现的,那就让我们刨根问底的看一看,g++是如何利用vptr和vtbl实现的。

第一步,使用-fdump-class-hierarchy参数导出g++生成的类内存结构:

 Vtable Base1
  Base1:: _ZTV5Base1: 3 u条目
  0 (int (*) (…)) 0
  4 (int (*) (…)),_ZTI5Base1)
  8 Base1: f
  
  类Base1
  大?4=4一致
  基地大?4基地对齐=4
  Base1 (0 xb6acb438) 0几乎空无一人
  vptr=((,Base1: _ZTV5Base1) + 8 u)
  
  的Vtable Base2
  Base2:: _ZTV5Base2: 3 u条目
  0 (int (*) (…)) 0
  4 (int (*) (…)),_ZTI5Base2)
  8 Base2: g
  
  类Base2
  大?4=4一致
  基地大?4基地对齐=4
  Base2 (0 xb6acb474) 0几乎空无一人
  vptr=((,Base2: _ZTV5Base2) + 8 u)
  
  Vtable为派生
  派生::_ZTV7Derived: 8 u条目
  0 (int (*) (…)) 0
  4 (int (*) (…)),_ZTI7Derived)
  8派生:f
  12派生:g
  16派生:h
  20 (int (*) (…)) 0 x000000004
  24 (int (*) (…)),_ZTI7Derived)
  28派生::_ZThn4_N7Derived1gEv
  
  类的派生
  大?8=4一致
  基础尺寸一致=4=8基地
  派生(0 xb6b12780) 0
  vptr=((,派生:_ZTV7Derived) + 8 u)
  Base1 (0 xb6acb4b0) 0几乎空无一人
  初选,派生(0 xb6b12780)
  Base2 (0 xb6acb4ec) 4几乎空无一人
  vptr=((,派生:_ZTV7Derived) + 28 u) 

如果看不明白这些乱七八糟的输出,没关系(当然能看懂更好),把上面的输出转换成图的形式就清楚了:

 c++虚函数在g++中的实现方法

vptr和vtbl

其中有几点尤其值得注意:

<李>

我用来测试的机器是32位机,所有vptr占4个字节,每个vtbl中的函数指针也是4个字节

c++虚函数在g++中的实现方法