虚函数的小秘密

本文分析虚函数的小秘密,通过几个case说明为了支持虚函数,应该有什么样的约定,生成什么样的代码。

C++中虚函数用于实现多态:即方法调用和对象的动态类型绑定。

详细地说对A*类型指针p指向A的公有派生类B的对象,A中有虚函数foo,B中给定foo的还有一份实现,p->foo应该和B中的新实现绑定,而不是和A中的实现绑定。

一般而言。会在对象布局中插入一个虚函数表指针。在表中列出了全部的虚函数。

以下以这样的模型为基础讨论。

先看基类A,假定有数据成员dataA,虚函数表指针vptrA。虚函数fooA()。

从fooA本身的实现来看,在thiscall的约定下,觉得ecx作为输入參数,当中的值是this指针,this指针的类型当然是A*了。

要调用虚函数则要满足例如以下条件:

1. 可以找到fooA的实现地址。

2. ecx中含有this,this的类型是A*(即this确实指向了A的对象,而不会是A的派生类的对象)。

在此,引入一个不变式:

假设有A* ptr;那么ptr指向的内存数据的理解应该全然由A这个类型决定。不管ptr指向的确实是是一个A的对象,或者A的派生类的对象。

既然如此,派生类对象应该存在某段区域,这段区域能够看到一个A的对象。ptr指向派生类时。应该指向派生类对象的这段区域。

[不变式使得向上向下转型时有指针重设,使得在ptr->foo的代码中能够断言ptr指向的对象是什么,使得能够用不变的几步操作来实现对foo的调用]

case 1

假设我们有A* ptr = new A();虚函数的调用实现应该是,从ptr指向的数据拿到虚表指针,依据偏移,进而拿到A::fooA的详细地址。将ptr直接放到ecx中。

所以满足fooA的调用条件能够满足。

假设有B继承自A,B中引入虚函数fooB,数据dataB。没有override了fooA。一个可能的内存布局是:

vptrB dataB vptrA dataA。

case 2

假设有A* ptr = new B();依据前面的不变式ptr会指向vptrA的位置。假设在vptr中fooA的位置放上fooA实现的地址。这个时候调用方法没问题,和前面讨论的一样。

case 3

假设有B* ptr = new B();依据类型信息B,通过偏移量拿到vptrA。进而能拿到fooA的地址。此外,拿到vptrA的同一时候,也拿到了A-sub-object的地址。将这个sub object的地址放到ecx中,于是虚函数调用条件满足。

再考虑B中override了fooA的情况,最好还是设这个override的函数名为fooA_override_by_B:在构造B的时候。将fooA_override_by_B填入了vptrA中相应位置。

case 4

对于A* ptr = new B();传入的ecx指向的是A这个sub-object,实际调用到的函数是fooA_override_by_B。而依据虚函数调用条件,fooA_override_by_B会觉得传入的是B*。

所以fooA_override_by_B分为两部分,当中一部分fooA_override_by_B_impl是详细实现,会觉得传入的this是B*的。还有一部分fooA_override_by_B_adjust会将传入的A*调整为B*,然后跳转到fooA_override_by_B_impl。

vptrA中放的应该是fooA_override_by_B_adjust。

case 5

对于B* ptr = new B();在外部将ecx指向了A-sub-object,在fooA_override_by_B_adjust中又将指向A-sub-object的对象调整为指向B的对象。

所以。在发生override时,一方面提供相应的实现函数,这个函数接受正在override的类的指针。还有一方面在被override的类的虚函数表中。放上调整函数,将父类指针调整为子类指针。这样做是可行的,由于被override的类和正在override的类的信息是编译时确定的。

讨论到这里,我们在有T* ptr时,调用虚函数的条件是这样满足的:

1. 依据T这个类型。及其继承关系。考察被调用的虚函数,找到相应的虚表指针。然后依据虚函数在虚表中的位置。确定虚函数的地址。

当中T的类型,继承关系,被调用的虚函数,某个类引入的虚函数在虚表中的位置,ptr的值,这5个量是已知的。未知的是虚函数的位置。

2. 相同被调用的虚函数所属的类在T中的偏移也是个已知量,加上ptr就得到相应的sub-object的地址。

再看个多继承的样例,假设A,B作为基类。都有虚函数fooX。C继承自A,B。override了fooX。

这个时候C中有三个虚函数表,在A的虚函数表中fooX应该是fooX_A_override_by_C_adjust。在B的虚函数表中foo应该是fooX_B_override_by_C_adjust。

和前面的不同,我们用fooX_A表示fooX是A中的,fooX_B是B中的,两者名字同样。我们用这个后缀以作差别。两个adjust函数能够分别将A和B的指针调整为C的指针。然后分别都跳转到fooX_A_override_by_C_impl,fooX_B_override_by_C_impl实际上。这两个impl函数是同一个。当然。第三个虚函数表是C自己能够引入的虚函数,在此不影响讨论。

虚函数有什么秘密?

1.[指针约定、对象布局]形如T* ptr;应当觉得指向的内存開始sizeof(T)个字节确实是一个T的对象。

这就要求向上或向下转型时。有指针重设。在对象布局中子类中存在某段区域。这段区域正好是基类对象。

2.[引入虚函数表]对象中有指针指向虚函数表。使得在指向不同的虚函数表的时候,虚函数调用有不同的表现。

3.[虚函数表中的项对this的需求]在call虚函数表的某一项时,ecx中保存的是虚函数表相应的对象的this指针。

4.[override后的this改动]在子类override时,对于每一处须要改动的虚函数表(一般仅仅有一个),因为3满足,所以能够插入将这个对象转为子类对象的转换代码。然后跳转到override后的实现。

5.[可能override多个虚函数表中的项]有多个须要改动的虚函数表时说明通过该对象同一时候override多个实现。

注:虚函数表共享在里没有被提及,可是并不影响分析。

全部的这些结果,依据经验推算而来,不代表实现中一定是这样。

时间: 2024-10-16 10:44:09

虚函数的小秘密的相关文章

C++ 虚函数和虚继承浅析

本文针对C++里的虚函数,虚继承表现和原理进行一些简单分析,有希望对大家学习C++有所帮助.下面都是以VC2008编译器对这两种机制内部实现为例. 虚函数 以下是百度百科对于虚函数的解释: 定义:在某基类中声明为 virtual 并在一个或多个派生类中被重新定 义的成员函数[1] 语法:virtual 函数返回类型 函数名(参数表) { 函数体 } 用途:实现多态性,通过指向派生类的基类指针,访问派生类中同名覆盖成员函数 函数声明和定义和普通的类成员函数一样,只是在返回值之前加入了关键字"vir

单继承与多继承中的虚函数表和虚函数指针

首先,我们了解一下何为单继承,何为多继承?? 单继承:一个子类只有一个直接父类. 多继承:一个子类有两个或多个直接父类. 单继承中的虚函数表分析: 示例程序: #include <iostream> using namespace std; typedef void(*FUNC)(); class Base { public: virtual void func1() { cout << "Base::func1()" << endl; } virt

C++:纯虚函数与抽象类

5.4.3 纯虚函数和抽象类 纯虚函数是一个在基类中说明的虚函数,它在该基类中没有定义,但是要求在派生类中根据需要对它进行定义,或仍然说明为纯虚函数. 声明纯虚函数的一般格式是: virtual 函数类型 函数名(参数表)=0: 纯虚函数的作用是:在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行重新定义.纯虚函数没有函数体,它最后面“=0 ”并不表示函数的返回值是0,它只是形式上的作用,告诉编译系统这是纯虚函数.纯虚函数不具有函数的功能,不能被调用. //应用举例 #includ

含有虚函数的类sizeof大小

#include <iostream> using namespace std; class Base1{ virtual void fun1(){} virtual void fun11(){} public: virtual ~Base1(); }; class Base2{ virtual void fun2(){} }; class DerivedFromOne: public Base2 { virtual void fun2(){} virtual void fun22(){} }

C++沉思录之二——虚函数使用的时机

虚函数使用的时机 为什么虚函数不总是适用? 1. 虚函数有事会带来很大的消耗: 2. 虚函数不总是提供所需的行为: 3. 当我们不考虑继承当前类时,不必使用虚函数. 必须使用虚函数的情况: 1. 当你想删除一个表面上指向基类对象,实际却是指向派生类对象的指针,就需要虚析构函数. C++沉思录之二--虚函数使用的时机,布布扣,bubuko.com

C++中virtual(虚函数)的用法

转载:http://blog.csdn.net/foreverhuylee/article/details/34107615 在面向对象的C++语言中,虚函数(virtual function)是一个非常重要的概念. 什么是虚函数: 虚函数是指一个类中你希望重载的成员函数 ,当你用一个  基类指针或引用   指向一个继承类对象的时候,调用一个虚函数时, 实际调用的是继承类的版本. --摘自MSDN                                                 

C++抽象类与纯虚函数实战举例

虚函数与纯虚函数请参考:http://blog.csdn.net/hackbuteer1/article/details/7558868 #include <iostream> using namespace std; class Animal { public:     virtual void eat()=0;     virtual void sleep()=0; }; class Cat:public Animal { public:     void eat()     {     

c++ 虚函数详解

下面是对C++的虚函数的理解. 一,定义 简单地说,那些被virtual关键字修饰的成员函数,就是虚函数.虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离:用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略.下面来看一段简单的代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 classA { publi

多态&amp;虚函数

(1).对象类型: a.静态类型:对象声明时的类型,编译的时候确定 b.动态类型:对象的类型是运行时才能确定的 class A {}; class B:public A {}; int main() { B* b; A* a=b;//a的静态类型是A*,动态类型(运行时)类型是B* return 0; } (2).多态 a.静态多态:函数重载.泛性编程 int Add(int a,int b) { return a+b; } float Add(float a,float b) { return