C++虚函数表的工作原理

合集下载

虚函数原理

虚函数原理

虚函数原理虚函数是 C++ 中一个非常重要的特性,它为面向对象编程提供了很强的支持。

虚函数的实现原理是通过虚函数表实现的,本文将介绍虚函数的概念、使用方法以及实现原理。

一、虚函数概念虚函数是指在基类中使用 virtual 关键字声明的成员函数,它的作用是允许在子类中对该函数进行覆盖。

具体来说,虚函数允许在子类中定义一个与基类中同名的函数,当使用子类对象调用该函数时,程序会动态的选择调用子类中的函数。

虚函数的语法如下:```class Base {public:virtual void foo();};```虚函数可以被重写(覆盖),也可以被继承,但是不能被 static 和 friend 修饰。

二、虚函数的使用使用虚函数需要满足一下条件:1.虚函数必须在公有的类成员函数列表中声明,并在类声明的内部定义。

2.虚函数必须在基类和派生类中以相同的参数列表进行定义。

下面是一个使用虚函数的简单例子:class Square: public Shape {public:Square(double s) : side(s) {}double getArea() { return side * side; }Shape 是一个基类,Square 是它的一个派生类,Square 中重写了 getArea() 函数,计算正方形的面积。

虚函数的实现原理是通过虚函数表实现的。

虚函数表是一个指针数组,存储了每个类中的虚函数指针。

当对象被创建时,会在其内存空间中创建一个指向虚函数表的指针,这个指针通常称为虚函数表指针(vptr),虚函数的调用就是通过这个指针完成的。

每个含有虚函数的类都有一个独立的虚函数表,虚函数表智能在类的第一个对象中存储,它包含了该类中所有虚函数的地址。

在派生类中,虚函数表通常继承自它的直接基类,并在此基础上添加或修改虚函数的地址。

这样如果在派生类对象中调用虚函数时,程序会先获得对象的虚函数表指针,然后通过该指针找到对应的虚函数地址来执行函数。

C++虚函数及虚函数表解析

C++虚函数及虚函数表解析

C++虚函数及虚函数表解析虚函数的定义: 虚函数必须是类的⾮静态成员函数(且⾮构造函数),其访问权限是public(可以定义为private or proteceted,但是对于多态来说,没有意义。

),在基类的类定义中定义虚函数的⼀般形式: virtual 函数返回值类型虚函数名(形参表) { 函数体 } 虚函数的作⽤是实现动态联编,也就是在程序的运⾏阶段动态地选择合适的成员函数,在定义了虚函数后, 可以在基类的派⽣类中对虚函数重新定义(形式也是:virtual 函数返回值类型虚函数名(形参表){ 函数体 }),在派⽣类中重新定义的函数应与虚函数具有相同的形参个数和形参类型。

以实现统⼀的接⼝,不同定义过程。

如果在派⽣类中没有对虚函数重新定义,则它继承其基类的虚函数。

当程序发现虚函数名前的关键字virtual后,会⾃动将其作为动态联编处理,即在程序运⾏时动态地选择合适的成员函数。

实现动态联编需要三个条件: 1、必须把需要动态联编的⾏为定义为类的公共属性的虚函数。

2、类之间存在⼦类型关系,⼀般表现为⼀个类从另⼀个类公有派⽣⽽来。

3、必须先使⽤基类指针指向⼦类型的对象,然后直接或者间接使⽤基类指针调⽤虚函数。

定义虚函数的限制: (1)⾮类的成员函数不能定义为虚函数,类的成员函数中静态成员函数和构造函数也不能定义为虚函数,但可以将析构函数定义为虚函数。

实际上,优秀的程序员常常把基类的析构函数定义为虚函数。

因为,将基类的析构函数定义为虚函数后,当利⽤delete删除⼀个指向派⽣类定义的对象指针时,系统会调⽤相应的类的析构函数。

⽽不将析构函数定义为虚函数时,只调⽤基类的析构函数。

(2)只需要在声明函数的类体中使⽤关键字“virtual”将函数声明为虚函数,⽽定义函数时不需要使⽤关键字“virtual”。

(3)如果声明了某个成员函数为虚函数,则在该类中不能出现和这个成员函数同名并且返回值、参数个数、参数类型都相同的⾮虚函数。

C++中虚函数工作原理和(虚)继承类的内存占用大小计算

C++中虚函数工作原理和(虚)继承类的内存占用大小计算

C++中虚函数工作原理和(虚)继承类的内存占用大小计算一、虚函数的工作原理虚函数的实现要求对象携带额外的信息,这些信息用于在运行时确定该对象应该调用哪一个虚函数。

典型情况下,这一信息具有一种被称为vptr(virtual table pointer,虚函数表指针)的指针的形式。

vptr 指向一个被称为vtbl(virtual table,虚函数表)的函数指针数组,每一个包含虚函数的类都关联到vtbl。

当一个对象调用了虚函数,实际的被调用函数通过下面的步骤确定:找到对象的vptr 指向的vtbl,然后在vtbl 中寻找合适的函数指针。

虚拟函数的地址翻译取决于对象的内存地址,而不取决于数据类型(编译器对函数调用的合法性检查取决于数据类型)。

如果类定义了虚函数,该类及其派生类就要生成一张虚拟函数表,即vtable。

而在类的对象地址空间中存储一个该虚表的入口,占4个字节,这个入口地址是在构造对象时由编译器写入的。

所以,由于对象的内存空间包含了虚表入口,编译器能够由这个入口找到恰当的虚函数,这个函数的地址不再由数据类型决定了。

故对于一个父类的对象指针,调用虚拟函数,如果给他赋父类对象的指针,那么他就调用父类中的函数,如果给他赋子类对象的指针,他就调用子类中的函数(取决于对象的内存地址)。

虚函数需要注意的大概就是这些个地方了,之前在More effective C++上好像也有见过,不过这次在Visual C++权威剖析这本书中有了更直白的认识,这本书名字很牛逼,看看内容也就那么回事,感觉名不副实,不过说起来也是有其独到之处的,否则也没必要出这种书了。

每当创建一个包含有虚函数的类或从包含有虚函数的类派生一个类时,编译器就会为这个类创建一个虚函数表(VTABLE)保存该类所有虚函数的地址,其实这个VTABLE的作用就是保存自己类中所有虚函数的地址,可以把VTABLE形象地看成一个函数指针数组,这个数组的每个元素存放的就是虚函数的地址。

CPP笔试面试汇总

CPP笔试面试汇总

IT公司笔试面试题系列(一)C++笔试题1.多态类中的虚函数表是Compile-Time,还是Run-Time时建立的?答案:虚拟函数表是在编译期就建立了,各个虚拟函数这时被组织成了一个虚拟函数的入口地址的数组.而对象的隐藏成员--虚拟函数表指针是在运行期--也就是构造函数被调用时进行初始化的,这是实现多态的关键.2.一个父类写了一个virtual 函数,如果子类覆盖它的函数不加virtual ,也能实现多态?在子类的空间里,有没有父类的这个函数,或者父类的私有变量? (华为笔试题)答案:只要基类在定义成员函数时已经声明了virtue关键字,在派生类实现的时候覆盖该函数时,virtue关键字可加可不加,不影响多态的实现。

子类的空间里有父类的所有变量(static除外)。

3.完成字符串拷贝可以使用sprintf、strcpy 及memcpy 函数,请问这些函数有什么区别,你喜欢使用哪个,为什么?答案:这些函数的区别在于实现功能以及操作对象不同。

1.strcpy 函数操作的对象是字符串,完成从源字符串到目的字符串的拷贝功能。

2.snprintf 函数操作的对象不限于字符串:虽然目的对象是字符串,但是源对象可以是字符串、也可以是任意基本类型的数据。

这个函数主要用来实现(字符串或基本数据类型)向字符串的转换功能。

如果源对象是字符串,并且指定 %s 格式符,也可实现字符串拷贝功能。

3.memcpy 函数顾名思义就是内存拷贝,实现将一个内存块的内容复制到另一个内存块这一功能。

内存块由其首地址以及长度确定。

程序中出现的实体对象,不论是什么类型,其最终表现就是在内存中占据一席之地(一个内存区间或块)。

因此,memcpy 的操作对象不局限于某一类数据类型,或者说可适用于任意数据类型,只要能给出对象的起始地址和内存长度信息、并且对象具有可操作性即可。

鉴于 memcpy 函数等长拷贝的特点以及数据类型代表的物理意义,memcpy 函数通常限于同种类型数据或对象之间的拷贝,其中当然也包括字符串拷贝以及基本数据类型的拷贝。

C#虚函数virtual详解

C#虚函数virtual详解
为子类需要去重新实现的操作(override),我们可以称之做“热点”。而虚拟函数也是 OOP
中实现多态的关键之一。
还是上面的例子(C#):
class 飞禽 { public string wing; // 翅膀 public string feather; // 羽毛 …… // 其它属性和行为 public virtual bool Fly() // 利用关键字 virtual 来定义为虚拟函数,这是一个热点 { // 空下来让子类去实现 } } class 麻雀 : 飞禽 // 麻雀从飞禽继承而来 { …… // 定义麻雀自己特有的属性和行为 public override bool Fly() // 利用关键字 override 重载飞翔动作,实现自己的飞翔 { …… // 实现麻雀飞的动作 } } class 鹤 : 飞禽 // 鹤从飞禽继承而来 { …… // 定义鹤自己的特有的属性和行为 public override bool Fly() // 利用关键字 override 重载实现鹤的飞翔 {
d = new D(); // 实例化 d 对象,D 是 d 的实例类 a.Func(); // 执行 a.Func:1.先检查申明类 A 2.检查到是虚拟方法 3.转去检查实例 类 A,就为本身 4.执行实例类 A 中的方法 5.输出结果 Func In A b.Func(); // 执行 b.Func:1.先检查申明类 A 2.检查到是虚拟方法 3.转去检查实例 类 B,有重载的 4.执行实例类 B 中的方法 5.输出结果 Func In B c.Func(); // 执行 c.Func:1.先检查申明类 A 2.检查到是虚拟方法 3.转去检查实例 类 C,无重载的 4.转去检查类 C 的父类 B,有重载的 5.执行父类 B 中的 Func 方法 5.输 出结果 Func In B d.Func(); // 执行 d.Func:1.先检查申明类 A 2.检查到是虚拟方法 3.转去检查实例 类 D,无重载的(这个地方要注意了,虽然 D 里有实现 Func(),但没有使用 override 关键 字,所以不会被认为是重载) 4.转去检查类 D 的父类 A,就为本身 5.执行父类 A 中的 Func 方法 5.输出结果 Func In A D d1 = new D(); d1.Func(); // 执行 D 类里的 Func(),输出结果 Func In D Console.ReadLine(); } } } 3.

50个C语言C++常见面试题及答案

50个C语言C++常见面试题及答案

C/C++经典面试题面试题1:变量的声明和定义有什么区别为变量分配地址和存储空间的称为定义,不分配地址的称为声明。

一个变量可以在多个地方声明,但是只在一个地方定义。

加入extern修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分定义。

说明:很多时候一个变量,只是声明不分配内存空间,直到具体使用时才初始化,分配内存空间,如外部变量。

面试题2:写出bool 、int、float、指针变量与“零值”比较的if语句bool型数据:if( flag ){A;}else{B;}int型数据:if( 0 != flag ){A;}else{B;}指针型数:if( NULL == flag ){A;}else{B;}float型数据:if ( ( flag >= NORM ) && ( flag <= NORM ) ){A;}注意:应特别注意在int、指针型变量和“零值”比较的时候,把“零值”放在左边,这样当把“==”误写成“=”时,编译器可以报错,否则这种逻辑错误不容易发现,并且可能导致很严重的后果。

面试题3:sizeof和strlen的区别sizeof和strlen有以下区别:❑sizeof是一个操作符,strlen是库函数。

❑sizeof的参数可以是数据的类型,也可以是变量,而strlen只能以结尾为‘\0‘的字符串作参数。

❑编译器在编译时就计算出了sizeof的结果。

而strlen函数必须在运行时才能计算出来。

并且sizeof 计算的是数据类型占内存的大小,而strlen计算的是字符串实际的长度。

❑数组做sizeof的参数不退化,传递给strlen就退化为指针了。

注意:有些是操作符看起来像是函数,而有些函数名看起来又像操作符,这类容易混淆的名称一定要加以区分,否则遇到数组名这类特殊数据类型作参数时就很容易出错。

最容易混淆为函数的操作符就是sizeof。

面试题4:C语言的关键字static 和C++ 的关键字static 有什么区别在C中static用来修饰局部静态变量和外部静态变量、函数。

C++之普通成员函数、虚函数以及纯虚函数的区别与用法要点

C++之普通成员函数、虚函数以及纯虚函数的区别与用法要点

C++之普通成员函数、虚函数以及纯虚函数的区别与⽤法要点普通成员函数是静态编译的,没有运⾏时多态,只会根据指针或引⽤的“字⾯值”类对象,调⽤⾃⼰的普通函数;虚函数为了重载和多态的需要,在基类中定义的,即便定义为空;纯虚函数是在基类中声明的虚函数,它可以再基类中有定义,且派⽣类必须定义⾃⼰的实现⽅法。

假设我们有三个类Person、Teacher、Student它们之间的关系如下:类的关系图普通成员函数【Demo1】根据这个类图,我们有下⾯的代码实现#ifndef __OBJEDT_H__#define __OBJEDT_H__#include <string>#include <iostream>class Person{public:Person(const string& name, int age) : m_name(name), m_age(age){}void ShowInfo(){cout << "姓名:" << m_name << endl;cout << "年龄:" << m_age << endl;}protected:string m_name; //姓名int m_age; //年龄};class Teacher : public Person{public:Teacher(const string& name, int age, const string& title): Person(name, age), m_title(title){}void ShowInfo(){cout << "姓名:" << m_name << endl;cout << "年龄:" << m_age << endl;cout << "职称:" << m_title << endl;}private:string m_title; //职称};class Student : public Person{public:Student(const string& name, int age, int studyId): Person(name, age), m_studyId(studyId){}void ShowInfo(){cout << "姓名:" << m_name << endl;cout << "年龄:" << m_age << endl;cout << "学号:" << m_studyId << endl;}private:int m_studyId; //学号};#endif //__OBJEDT_H__测试代码:void test(){Person* pPerson = new Person("张三", 22);Teacher* pTeacher = new Teacher("李四", 35, "副教授");Student* pStudent = new Student("王五", 18, 20151653);pPerson->ShowInfo();cout << endl;pTeacher->ShowInfo();cout << endl;pStudent->ShowInfo();cout << endl;delete pPerson;delete pTeacher;delete pStudent;}结果:姓名:张三年龄:22姓名:李四年龄:35职称:副教授姓名:王五年龄:18学号:20151653说明:这⾥的ShowInfo就是⼀个普通的函数。

实验七 虚函数及应用

实验七  虚函数及应用

实验七虚函数及应用一、实验目的1.理解虚函数与运行时(动态)多态性之间的关系,掌握虚函数的定义及应用;2.理解纯虚函数与抽象类的概念,掌握抽象类的定义及应用;3.理解虚析构函数的概念及作用。

二、实验学时课内实验:2课时课外练习:2课时三本实验涉及的新知识㈠虚函数与动态多态性在C++中,如果将基类与派生类的同名成员函数定义为虚函数,就可以定义一个基类指针,当基类指针指向基类对象时访问基类的成员函数,当基类指针指向派生类对象时访问派生类的成员函数,实现在运行时根据基类指针所指向的对象动态调用成员函数,实现动态多态性。

换句话说,虚函数与派生类相结合,使C++能支持运行时(动态)多态性,实现在基类中定义派生类所拥有的通用“接口”,而在派生类中定义具体的实现方法,即“一个接口,多种方法”。

㈡虚函数的定义1.在基类中定义在定义函数的前面加上“virtual ”。

即:virtual 返回类型函数名(参数表){ …… }2.在派生类中定义函数的返回类型、函数名、参数的个数、参数类型及顺序必须与基类中的原型完全相同。

3.说明:⑴在派生类中定义虚函数时,可用“virtual”也可不用“virtual”(最好都使用)。

⑵虚函数在派生类中重新定义时,其原型必须与基类中相同。

⑶必须用基类指针访问虚函数才能实现运行时(动态)多态性;当用普通成员函数的调用方法(即用圆点运算符)调用虚函数时,为静态调用;⑷虚函数在自身类中必须声明为成员函数(不能为友元函数或静态成员函数),但在另一个类中可以声明为友元函数。

⑸虚函数可以公有继承多次,其虚函数的特性不变。

⑹构造函数不能定义为虚函数,但析构函数可以定义为虚函数。

⑺虚函数与重载函数的关系①普通函数重载是通过参数类型或参数的个数不同实现的;重载一个虚函数时,其函数原型(返回类型、参数个数、类型及顺序)完全相同。

②当重载的虚函数只有返回类型不同时,系统将给出错误信息;如果定义的虚函数只有函数名相同,而参数个数或类型不同时,则为普通函数重载。

虚函数表的工作原理

虚函数表的工作原理

虚函数表的工作原理
虚函数表(virtual function table),也称为虚表(vtable),是
C++语言中实现动态多态性的机制之一。

虚函数表是每个带有
虚函数的类的特殊数据结构,它存储了该类的虚函数的地址。

当一个类定义了虚函数时,编译器会为该类生成一个虚函数表。

虚函数表是一个静态数组,其中的每个元素是一个函数指针,指向对应的虚函数实现代码的地址。

每个对象实例都包含一个指向该类对应的虚函数表的指针。

这个指针通常被称为虚函数指针或虚表指针。

通过这个指针,程序可以访问到正确的虚函数表,并调用相应的虚函数。

当调用一个虚函数时,实际上是通过对象的虚函数指针找到对象的虚函数表,然后再根据函数在虚函数表中的索引找到对应的函数指针,最后通过函数指针调用虚函数的实现代码。

这个过程被称为虚函数的动态绑定或运行时多态性。

使用虚函数表的好处是,它使得在继承关系中,子类可以重写父类的虚函数,并且通过父类指针或引用调用虚函数时,能够正确地调用到子类中的虚函数实现。

这样可以实现多态性,使得程序在运行时能够根据实际对象类型动态地调用适当的函数。

虚 函 数

虚 函 数
(5)虽然使用对象名和点运算符的方式也可以调用虚函 数 , 例 如 语 句 “ dl.print();” 可 以 调 用 虚 函 数 derived1∷print(),但是这种调用是在编译时进行的 是静态联编,它没有充分利用虚函数的特性。只有通 过基类指针访问虚函数时才能获得运行时的多态性。
(6)一个虚函数无论被公有继承多少次,它仍然保持其 虚函数的特性。
my_base--------
10 20
从程序运行的结果可以看出,虽然执行语句mp=&mc;后, 指针mp已经指向了对象mc,但是它所调用的函数(mp>show()),仍然是其基类对象的show(),显然这不是我 们所期望的。出现这个问题的原因以及解决方法,我 们将在下一节介绍。在此先说明引入派生类后,使用 对象指针应注意的几个问题:
derive op2; //定义派生类derive的对象op2
ptr=&op1;
//使指针ptr指向对象op1
ptr=&op2;
//错误,不允许将base类指针ptr指
向它的私有派生类//对象op2
//…
}
(2)允许将一个声明为指向基类的指针指向公有派生类
的对象,但是不能将一个声明为指向派生类对象的指 针指向其基类的对象。
(3)声明为指向基类的指针,当其指向公有派生的
对象时,只能用它来直接访问派生类中从基类继承来
的成员,而不能直接访问公有派生类中定义的成员,
例如:
class A { //... public: void print1(); }; class B:public A{ //... public: print2(); };
可见,虚函数同派生类的结合和使C++支持运行时的多 态性,而多态性对面向对象的程序设计是非常重要的, 实现了在基类定义派生类所拥有的通用接口,而在派 生类定义具体的实现方法,即常说的“同一接口,多 种方法”。

C语言 函数调用原理

C语言 函数调用原理

C语言函数调用原理
函数调用原理是指在C语言程序中,通过函数的调用来实现
代码的重用和模块化的编程方式。

函数调用原理主要涉及栈、函数调用过程和参数传递等方面。

在C语言中,当需要调用一个函数时,首先需要将函数的信
息压入栈中。

栈是一种后进先出(LIFO)的数据结构,用于
存储函数调用时产生的临时数据和函数调用的返回地址。

栈顶指针指向栈中当前可用的位置,当调用函数时,栈顶指针会向下移动,为函数的局部变量和参数分配空间。

当调用函数时,程序会将调用函数的返回地址压入栈中,并跳转到被调用函数的入口地址开始执行。

被调用函数执行完毕后,会通过返回指令将控制权和返回值返回到调用函数。

在函数调用过程中,还涉及参数的传递。

C语言中的参数传递
方式包括值传递、地址传递和指针传递。

对于简单类型的参数,如整型或字符型,一般采用值传递方式,即将参数的值复制一份传递给函数,不影响原始变量的值。

对于复杂类型参数,如数组或结构体,一般采用地址传递方式,即将参数的地址传递给函数,函数可以通过指针访问和修改参数的值。

总结起来,C语言的函数调用原理主要涉及栈、函数调用过程
和参数传递等方面。

通过函数的调用,可以实现代码的重用和模块化,提高程序的可读性和可维护性。

c++ 基类纯虚函数

c++ 基类纯虚函数

c++ 基类纯虚函数C++是一种广泛使用的编程语言,同时也是面向对象编程语言。

在C++中,一个类可以从另一个类继承,这个类被称为基类,而继承的类被称为派生类。

基类中的纯虚函数是C++中非常重要的概念之一,它们在设计类的继承层次结构时非常有用。

纯虚函数是一种在基类中定义的虚函数,它没有任何实现代码,只是为了被继承类实现。

纯虚函数可以用一对`virtual`和`= 0`来声明,例如:```virtual void MyFunction() = 0;```这个声明告诉编译器MyFunction是一个虚函数,并且没有实现,只是一个接口,继承类必须对其进行实现。

纯虚函数在基类中起到了规范和约束作用,因为派生类必须实现这个函数才能实现自己的功能。

在许多情况下,基类中的纯虚函数是被设计为通用算法,由派生类提供特定的实现。

这种方法被称为“模板方法”模式。

在一个简单的图形库中,我们可以定义一个基类Shape,这个基类包含一个纯虚函数`Draw()`和派生类Rectangle和Circle。

Rectangle和Circle分别提供它们自己的特殊化实现,Draw()方法则会被调用以完成具体的实际操作。

在C++中,派生类中的实现方法可以通过覆盖和重载来完成。

覆盖是指派生类重新定义基类中的虚函数,以提供不同的实现方法。

重载是指派生类定义具有相同名称的函数,但它们具有不同的参数列表,这使得可以在相同的类中实现两个或更多的函数。

在实际开发中,如果我们定义了一个纯虚函数但没有提供实现,那么它将无法被实例化,因为它是一个抽象的函数。

通常情况下,如果我们忘记实现这个函数,可能会在编译时收到一个错误消息。

在设计一个类的继承时,纯虚函数是一种非常有用的技术。

它可以帮助我们将代码和数据聚集在一起,以便更好地组织和管理。

纯虚函数还可以使我们更迅速和简单地实现代码的重用和复用。

在C++中,基类中的纯虚函数是非常重要的。

它们可以帮助我们在类的继承层次结构中实现一些非常有用的功能,例如模板方法和多态。

c语言虚函数

c语言虚函数

C语言虚函数中的特定函数简介C语言是一种面向过程的编程语言,并不直接支持面向对象的概念,其中包括了“类”、“对象”、“继承”等概念。

然而,通过使用一些技巧和设计模式,我们可以在C语言中实现类似于面向对象的功能,其中一个重要的概念就是虚函数。

虚函数是一种特殊的函数,它可以在派生类中被重写,从而实现多态。

虚函数的定义、用途和工作方式是C语言中面向对象编程的重要部分,本文将详细介绍这些内容。

虚函数的定义在C语言中,虚函数的定义需要使用函数指针和结构体实现。

我们可以使用函数指针将一个函数地址赋值给一个结构体中的成员变量,从而形成一个具有特定功能的“方法”。

这样,我们就可以通过这个函数指针来调用结构体中的函数,实现类似于面向对象中对象的方法调用的功能。

下面是一个虚函数的定义示例:typedef struct {void (*function_ptr)(void);} VTable;void function1(void) {printf("This is function1\n");}void function2(void) {printf("This is function2\n");}VTable vtable = {.function_ptr = function1};在上述示例中,我们使用typedef定义了一个VTable结构体,其中有一个function_ptr成员变量,它是一个指向函数的指针。

我们定义了两个函数function1和function2,并分别赋值给了vtable中的function_ptr成员变量。

虚函数的用途虚函数的主要用途是实现多态,使不同类型的对象可以调用相同的接口名称,但执行不同的操作。

通过使用虚函数,我们可以在C语言中实现类似于面向对象的继承和多态的功能。

在面向对象的编程中,我们可以定义一个基类(或接口),然后派生出不同的子类,每个子类都可以重写基类的虚函数,以实现它们自己的特定行为。

C++虚函数表的工作原理

C++虚函数表的工作原理

虚函数表工作原理C++中的虚函数的作用主要是实现了多态的机制。

关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。

这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。

所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。

比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。

关于虚函数的使用方法,我在这里不做过多的阐述。

大家可以看看相关的C++的书籍。

在这篇文章中,我只想从虚函数的实现机制上面为大家一个清晰的剖析。

当然,相同的文章在网上也出现过一些了,但我总感觉这些文章不是很容易阅读,大段大段的代码,没有图片,没有详细的说明,没有比较,没有举一反三。

不利于学习和阅读,所以这是我想写下这篇文章的原因。

也希望大家多给我提意见。

言归正传,让我们一起进入虚函数的世界。

虚函数表对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。

简称为V-Table。

在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。

这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。

这里我们着重看一下这张虚函数表。

在C++的标准规格说明书中说到,编译器必需要保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确取到虚函数的偏移量)。

这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。

听我扯了那么多,我可以感觉出来你现在可能比以前更加晕头转向了。

没关系,下面就是实际的例子,相信聪明的你一看就明白了。

假设我们有这样的一个类:class Base {public:virtual void f() { cout << "Base::f" << endl; }virtual void g() { cout << "Base::g" << endl; }virtual void h() { cout << "Base::h" << endl; }};按照上面的说法,我们可以通过Base的实例来得到虚函数表。

虚函数和虚基类的区别

虚函数和虚基类的区别

虚函数和虚基类的区别 C++虚函数,纯虚函数,抽象类以及虚基类的区别Part1.C++中的虚函数什么是虚函数:直观表达就是,如果⼀个函数的声明中有 virtual 关键字,那么这个函数就是虚函数。

虚函数的作⽤:虚函数的最⼤作⽤就是实现⾯向对象程序设计的⼀⼤特点,多态性,多态性表达的是⼀种动态的概念,是在函数调⽤期间,进⾏动态绑定,以达到什么样的对象就实现什么样的功能的效果。

虚函数的⼀般声明语法:virtual 函数类型函数名 (形参表)注意:虚函数的声明只能出现在类的定义中,不能出现在成员函数实现的时候虚函数⼀般不声明为内联函数,但是声明为内联函数也不会引起错误在运⾏过程中要实现多态的三个条件:类之间满⾜赋值兼容关系(也就是类之间有继承关系)要声明为虚函数调⽤虚函数时,要由成员函数或者是指针和引⽤来访问代码举例#include <iostream>using namespace std;class Base1 {public:public:virtual void play();};void Base1::play(){cout << "Base1::play()" << endl;}class Base2:public Base1{virtual void play();};void Base2::play() {cout << "Base2::play()" << endl;}class Derived :public Base2{virtual void play();};void Derived::play() {cout << "Derived:: play()" << endl;}void fun(Base1* ba) { //声明⼀个基类的指针ba->play();}int main(){Base1 ba1;Base2 ba2;Derived de;//分别⽤不同的对象指针来调⽤ fun 函数fun(&ba1);fun(&ba2);fun(&de);return 0;}这代码含义就充分体现了虚函数作为实现多态条件的原因,由于 Base1 是 Base2 和 Derived 的⽗类,所以,Base1 是可以兼容 Base2 和Derived 的,所以在 fun 函数这⾥是⽤的 Base1 的指针来作为形参,不同的是当我传⼊参数不同时,fun 函数执⾏的是不同的结果,这就体现了多态的效果,我需要那个类型的实例,他就执⾏那个实例对应的⽅法。

C#的虚函数解析机制

C#的虚函数解析机制

C#的虚函数解析机制前⾔ 这篇⽂章出⾃我个⼈对C#虚函数特性的研究和理解,未参考、查阅第三⽅资料,因此很可能存在谬误之处。

我在这⾥只是为了将我的理解呈现给⼤家,也希望⼤家在看到我犯了错误后告诉我。

⽤词约定“⽅法的签名”包括返回类型、⽅法名、参数列表,这三者共同标识了⼀个⽅法。

“声明⽅法”,即指出该⽅法的签名。

“定义⽅法”,则是指定调⽤⽅法时执⾏的代码。

“同名⽅法”是指⽅法的签名相同的两个⽅法。

“重写”⼀个⽅法,意味着⼦类想继承⽗类对⽅法的声明,却想重新定义该⽅法。

单独使⽤“使⽤”⼀词时,包括“显式”或“隐式”两种使⽤⽅式:前者是指在代码中指明,后者是根据语句的上下⽂推断。

某个类的⽅法,包括了在该类中定义的⽅法,以及由继承得到的直接⽗类的⽅法。

注意这条规则的递归性质。

理论部分 在⽗类与⼦类⾥,除了类之间的继承链,还存在⽅法之间的继承链。

C#⾥,在⼀个类中声明⼀个⽅法时,有四个和⽅法的继承性有关的关键字:new、virtual、sealed、override。

virtual表⽰允许⼦类的同名⽅法与其①建⽴继承链。

override表⽰其①与⽗类的同名⽅法之间建⽴了继承链,并隐式使⽤virtual关键字。

new表⽰其切断了其①与⽗类的同名⽅法之间的继承链。

sealed表⽰将其①与⽗类的同名⽅法建⽴继承链(注意这个就是override关键字的特性),并且不允许⼦类的同名⽅法与其建⽴继承链。

在使⽤sealed关键字时,必须同时显式使⽤override关键字。

以及:在定义⽅法时,若不使⽤以上关键字,⽅法就会具有new关键字的特性。

对于这⼀点,如果⽗类中没有同名⽅法,则没有任何影响;如果⽗类中存在⼀个同名⽅法,编译器会给出⼀个警告,询问你是否是想隐藏⽗类的同名⽅法,并推荐你显式地为其指定new关键字。

①其:指代正在进⾏声明的⽅法。

依照上述的说明,在调⽤类上的某个⽅法时,可以为该⽅法构建出⼀个或多个“⽅法继承链”。

C++程序设计基础第6章 虚函数与多态性

C++程序设计基础第6章 虚函数与多态性

6.2.1 虚函数的定义
2. 虚函数的定义 • 虚函数的定义是在基类中进行的 。 • 虚函数的定义语法格式如下:
virtual<函数类型><函数名>(形参表) {
函数体 }
12
6.2.1 虚函数的定义
3. 定义虚函数时,要注意遵循以下规则: 1)只有成员函数才能声明为虚函数,因为虚
函数仅适用于有继承关系的类对象,所以 普通函数不能声明为虚函数。 2)虚函数的声明只能出现在类声明中的函数 原型声明中,而不能出现在成员的函数体 实现上。 3)类的静态成员函数不可以定义为虚函数, 因为静态成员函数不受限于某个对象。
}
7
void main()
{
MaxClass mc(34,67,143,89);
cout<<"计算前两个数中的最大数为:“
<<mc.max(34,67)<<endl;
cout<<"计算前三个数中的最大数为:“
<<mc.max(34,67,143)<<endl;
cout<<"计算四个数中的最大数为:“
运行结果: 张晓强,园林工人 李文卓,生命科学教师
23
6.2.3 虚函数的重载
• 2. 多继承中的虚函数
【例6.8】多继承中使用虚函数例题。
#include <iostream.h>
class base1
//定义基类base1
{
public: virtual void display()
//函数定义为虚函数
运行结果:
(1) : 动物(食草/食肉). (2) : 食草动物 : 羚羊 (3) : 食草动物 : 羚羊 (4) : 食肉动物 : 老虎 (5) : 食肉动物 : 老虎 (6) : 食草动物 : 羚羊 (7) : 食肉动物 : 老虎

深入研究虚函数和vtable(超赞的一篇文档)

深入研究虚函数和vtable(超赞的一篇文档)

在面向对象的C++语言中,虚函数是一个非常重要的概念。

因为它充分体现了面向对象思想中的继承和多态性这两大特性,在C++语言里应用极广。

比如在微软的MFC类库中,你会发现很多函数都有virtual关键字,也就是说,它们都是虚函数。

难怪有人甚至称虚函数是C++语言的精髓。

那么,什么是虚函数呢,我们先来看看微软的解释:虚函数是指一个类中你希望重载的成员函数,当你用一个基类指针或引用指向一个继承类对象的时候,你调用一个虚函数,实际调用的是继承类的版本。

——摘自MSDN这个定义说得不是很明白。

MSDN中还给出了一个例子,但是它的例子也并不能很好的说明问题。

我们自己编写这样一个例子:#include "stdio.h"#include "conio.h"class Parent{public:char data[20];virtual void Function2; // 这里声明是虚函数}parent;void Parent::Function1{}void Parent::Function2{}class Child:public Parent{} child;{}{}int main(int argc, char* argv){Parent *p; // 定义一个基类指针if(_getch=='c') // 如果输入一个小写字母c p=&child; // 指向继承类对象elsep=&parent; // 否则指向基类对象 // 这里在编译时会直接给出的入口地址。

// 注意这里,执行的是哪一个?return 0;}用任意版本的Visual C++或Borland C++编译并运行,输入一个小写字母c,得到下面的结果:This is parent,function1This is child,function2为什么会有第一行的结果呢?因为我们是用一个Parent类的指针调用函数Fuction1,虽然实际上这个指针指向的是Child类的对象,但编译器无法知道这一事实(直到运行的时候,程序才可以根据用户的输入判断出指针指向的对象),它只能按照调用Parent类的函数来理解并编译,所以我们看到了第一行的结果。

C++ 虚函数[详讲]

C++ 虚函数[详讲]

什么是虚函数?简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。

为什么要引入虚函数?虚函数的作用是实现类的继承所体现的多态性,具体点是实现动态联编。

从程序的角度上来说,在定义了虚函数后,可以在派生类中对虚函数重新定义,以实现统一的接口,不同定义过程,在程序的运行阶段动态地选择合适的成员函数。

什么是多态性?简单点说,多态性是将接口与实现进行分离;C++实现运行时多态性的关键途径:在公有派生情况下,一个指向基类的指针可用来访问从基类继承的任何对象。

语法:普通函数的前面加上virtual[cpp]view plaincopyprint?1.virtual函数返回值类型虚函数名(形参表)2.{3.//函数体4.}虚函数的调用方式:只能通过指向基类的指针或基类对象的引用来调用虚函数调用语法:[cpp]view plaincopyprint?1.指向基类的指针变量名->虚函数名(实参表)2.基类对象的引用名. 虚函数名(实参表)注意:正常情况下,如果不把函数声明为虚函数,指向基类的指针的访问情况如下:1)基类指针指向基类对象:基类指针可以直接访问基类对象中的成员2)基类指针指向派生类对象:基类指针只能访问派生类中的从基类中继承的成员,派生类有同名的函数或成员,也只能调用基类的成员。

如果定义成虚函数时:定义一个基类指针,把不同的派生类对象付给它,会调用对应派生类的函数,而非基类函数。

举例:[cpp]view plaincopyprint?1.#include <iostream>ing namespace std;3.class A4.{5.public:6.virtual void show()7. {8. cout<<"A"<<endl;9. }10.};11.class B:public A12.{13.public:14.void show()15. {16. cout<<"B"<<endl;17. }18.};19.class C:public A20.{21.public:22.void show()23. {24. cout<<"C"<<endl;25. }26.};27.void main()28.{29. A*a;30. B b;31. C c;32. a=&b;33. a->show();34. a=&c;35. a->show();36. system("pause");37.}运行结果:B(换行)C(换行)--指向不同的派生类,调用不同的函数如果不加基类A中的Virtual,则输出结果:A(换行)A(换行)--基类指针,调用派生类中继承的基类成分定义虚函数,实现动态联编需要三个条件:1)必须把动态联编的行为定义为类的虚函数---定义虚函数2)类之间存在子类型关系,一般表现为一个类从另一个类公有派生而来---类之间是公有继承3)基类指针指向派生类的对象,然后使用基类指针调用虚函数注意:1、使用时,虚函数可以在基类中声明,提供界面。

dynamic_cast底层原理

dynamic_cast底层原理

dynamic_cast底层原理
在C++中,dynamic_cast是一种动态转型运算符,用于在运行时进行类型转换,特别是在处理继承关系和多态性的情况下非常有用。

dynamic_cast底层原理涉及到类型信息和虚表(virtual table)的概念。

在编译阶段,编译器会为每个类生成一个type_info对象,该对象存储了类的类型信息,包括类的名称和基类的类型信息。

这些type_info对象以一种层次结构组织,形成了类型的继承关系。

每个类中都会有一个虚表,虚表中存储了该类和其基类的虚函数指针,用于实现多态性。

当使用dynamic_cast进行类型转换时,编译器首先会检查目标类型是否相符,如果不相符则返回nullptr(对于指针类型)或者抛出一个bad_cast异常(对于引用类型)。

如果目标类型是多态的(即含有虚函数),编译器会根据类的虚表来进行类型检查。

在运行时,dynamic_cast会检查源对象指针的类型信息,然后与目标类型的类型信息进行比较。

如果源对象实际的类型与目标类型相符或者是目标类型的派生类,则返回成功转换后的指针或引用;否则返回nullptr(对于指针类型)或者抛出一个bad_cast异常(对于引用类型)。

需要注意的是,dynamic_cast只能用于具有多态性的类层次结构中,即含有虚函数的继承关系。

对于非多态的类,使用dynamic_cast会产生编译错误。

此外,dynamic_cast的运行时开销较大,尽量避免频繁使用。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

虚函数表工作原理C++中的虚函数的作用主要是实现了多态的机制。

关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。

这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。

所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。

比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。

关于虚函数的使用方法,我在这里不做过多的阐述。

大家可以看看相关的C++的书籍。

在这篇文章中,我只想从虚函数的实现机制上面为大家一个清晰的剖析。

当然,相同的文章在网上也出现过一些了,但我总感觉这些文章不是很容易阅读,大段大段的代码,没有图片,没有详细的说明,没有比较,没有举一反三。

不利于学习和阅读,所以这是我想写下这篇文章的原因。

也希望大家多给我提意见。

言归正传,让我们一起进入虚函数的世界。

虚函数表对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。

简称为V-Table。

在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。

这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。

这里我们着重看一下这张虚函数表。

在C++的标准规格说明书中说到,编译器必需要保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确取到虚函数的偏移量)。

这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。

听我扯了那么多,我可以感觉出来你现在可能比以前更加晕头转向了。

没关系,下面就是实际的例子,相信聪明的你一看就明白了。

假设我们有这样的一个类:class Base {public:virtual void f() { cout << "Base::f" << endl; } virtual void g() { cout << "Base::g" << endl; } virtual void h() { cout << "Base::h" << endl; } };按照上面的说法,我们可以通过Base的实例来得到虚函数表。

下面是实际例程:typedef void(*Fun)(void);Base b;Fun pFun = NULL;cout << "虚函数表地址:" << (int*)(&b) << endl;cout << "虚函数表—第一个函数地址:" <<(int*)*(int*)(&b) << endl;// Invoke the first virtual functionpFun = (Fun)*((int*)*(int*)(&b));pFun();实际运行经果如下:(Windows XP+VS2003, Linux 2.6.22 + GCC 4.1.3)虚函数表地址:0012FED4虚函数表—第一个函数地址:0044F148Base::f通过这个示例,我们可以看到,我们可以通过强行把&b转成int *,取得虚函数表的地址,然后,再次取址就可以得到第一个虚函数的地址了,也就是Base::f(),这在上面的程序中得到了验证(把int* 强制转成了函数指针)。

通过这个示例,我们就可以知道如果要调用Base::g()和Base::h(),其代码如下:(Fun)*((int*)*(int*)(&b)+0); // Base::f()(Fun)*((int*)*(int*)(&b)+1); // Base::g()(Fun)*((int*)*(int*)(&b)+2); // Base::h()这个时候你应该懂了吧。

什么?还是有点晕。

也是,这样的代码看着太乱了。

没问题,让我画个图解释一下。

如下所示:注意:在上面这个图中,我在虚函数表的最后多加了一个结点,这是虚函数表的结束结点,就像字符串的结束符“\0”一样,其标志了虚函数表的结束。

这个结束标志的值在不同的编译器下是不同的。

在WinXP+VS2003下,这个值是NULL。

而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下,这个值是如果1,表示还有下一个虚函数表,如果值是0,表示是最后一个虚函数表。

下面,我将分别说明“无覆盖”和“有覆盖”时的虚函数表的样子。

没有覆盖父类的虚函数是毫无意义的。

我之所以要讲述没有覆盖的情况,主要目的是为了给一个对比。

在比较之下,我们可以更加清楚地知道其内部的具体实现。

一般继承(无虚函数覆盖)下面,再让我们来看看继承时的虚函数表是什么样的。

假设有如下所示的一个继承关系:请注意,在这个继承关系中,子类没有重载任何父类的函数。

那么,在派生类的实例中,其虚函数表如下所示:对于实例:Derive d; 的虚函数表如下:我们可以看到下面几点:1)虚函数按照其声明顺序放于表中。

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

我相信聪明的你一定可以参考前面的那个程序,来编写一段程序来验证。

一般继承(有虚函数覆盖)覆盖父类的虚函数是很显然的事情,不然,虚函数就变得毫无意义。

下面,我们来看一下,如果子类中有虚函数重载了父类的虚函数,会是一个什么样子?假设,我们有下面这样的一个继承关系。

为了让大家看到被继承过后的效果,在这个类的设计中,我只覆盖了父类的一个函数:f()。

那么,对于派生类的实例,其虚函数表会是下面的一个样子:我们从表中可以看到下面几点,1)覆盖的f()函数被放到了虚表中原来父类虚函数的位置。

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

这样,我们就可以看到对于下面这样的程序,Base *b = new Derive();b->f();由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代,于是在实际调用发生时,是Derive::f()被调用了。

这就实现了多态。

多重继承(无虚函数覆盖)下面,再让我们来看看多重继承中的情况,假设有下面这样一个类的继承关系。

注意:子类并没有覆盖父类的函数。

对于子类实例中的虚函数表,是下面这个样子:我们可以看到:1)每个父类都有自己的虚表。

2)子类的成员函数被放到了第一个父类的表中。

(所谓的第一个父类是按照声明顺序来判断的)这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。

多重继承(有虚函数覆盖)下面我们再来看看,如果发生虚函数覆盖的情况。

下图中,我们在子类中覆盖了父类的f()函数。

下面是对于子类实例中的虚函数表的图:我们可以看见,三个父类虚函数表中的f()的位置被替换成了子类的函数指针。

这样,我们就可以任一静态类型的父类来指向子类,并调用子类的f()了。

如:Derive d;Base1 *b1 = &d;Base2 *b2 = &d;Base3 *b3 = &d;b1->f(); //Derive::f()b2->f(); //Derive::f()b3->f(); //Derive::f()b1->g(); //Base1::g()b2->g(); //Base2::g()b3->g(); //Base3::g()安全性每次写C++的文章,总免不了要批判一下C++。

这篇文章也不例外。

通过上面的讲述,相信我们对虚函数表有一个比较细致的了解了。

水可载舟,亦可覆舟。

下面,让我们来看看我们可以用虚函数表来干点什么坏事吧。

一、通过父类型的指针访问子类自己的虚函数我们知道,子类没有重载父类的虚函数是一件毫无意义的事情。

因为多态也是要基于函数重载的。

虽然在上面的图中我们可以看到Base1的虚表中有Derive的虚函数,但我们根本不可能使用下面的语句来调用子类的自有虚函数:Base1 *b1 = new Derive();b1->f1(); //编译出错任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法,所以,这样的程序根本无法编译通过。

但在运行时,我们可以通过指针的方式访问虚函数表来达到违反C++语义的行为。

(关于这方面的尝试,通过阅读后面附录的代码,相信你可以做到这一点)二、访问non-public的虚函数另外,如果父类的虚函数是private或是protected的,但这些非public的虚函数同样会存在于虚函数表中,所以,我们同样可以使用访问虚函数表的方式来访问这些non-public的虚函数,这是很容易做到的。

如:class Base {private:virtual void f() { cout << "Base::f" << endl; }};class Derive : public Base{};typedef void(*Fun)(void);void main() {Derive d;Fun pFun = (Fun)*((int*)*(int*)(&d)+0);pFun();}结束语C++这门语言是一门Magic的语言,对于程序员来说,我们似乎永远摸不清楚这门语言背着我们在干了什么。

需要熟悉这门语言,我们就必需要了解C++里面的那些东西,需要去了解C++中那些危险的东西。

不然,这是一种搬起石头砸自己脚的编程语言。

附录一:VC中查看虚函数表我们可以在VC的IDE环境中的Debug状态下展开类的实例就可以看到虚函数表了(并不是很完整的)附录二:例程下面是一个关于多重继承的虚函数表访问的例程:#include<iostream>using namespace std;class Base1 {public:virtual void f() { cout <<"Base1::f"<< endl; }virtual void g() { cout <<"Base1::g"<< endl; }virtual void h() { cout <<"Base1::h"<< endl; } };class Base2 {public:virtual void f() { cout <<"Base2::f"<< endl; }virtual void g() { cout <<"Base2::g"<< endl; }virtual void h() { cout <<"Base2::h"<< endl; } };class Base3 {public:virtual void f() { cout <<"Base3::f"<< endl; }virtual void g() { cout <<"Base3::g"<< endl; }virtual void h() { cout <<"Base3::h"<< endl; } };class Derive :public Base1,public Base2,public Base3 {public:virtual void f() { cout <<"Derive::f"<< endl; }virtual void g1() { cout <<"Derive::g1"<< endl; }};typedef void(*Fun)(void);int main(){Fun pFun = NULL;Derive d;int** pVtab = (int**)&d;//Base1's vtable//pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0);pFun = (Fun)pVtab[0][0];pFun();//pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1);pFun = (Fun)pVtab[0][1];pFun();//pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2); pFun = (Fun)pVtab[0][2];pFun();//Derive's vtable//pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3); pFun = (Fun)pVtab[0][3];pFun();//The tail of the vtablepFun = (Fun)pVtab[0][4];cout<<pFun<<endl;//Base2's vtable//pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0); pFun = (Fun)pVtab[1][0];pFun();//pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1); pFun = (Fun)pVtab[1][1];pFun();pFun = (Fun)pVtab[1][2];pFun();//The tail of the vtablepFun = (Fun)pVtab[1][3];cout<<pFun<<endl;//Base3's vtable//pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0); pFun = (Fun)pVtab[2][0];pFun();//pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1); pFun = (Fun)pVtab[2][1];pFun();pFun = (Fun)pVtab[2][2];pFun();//The tail of the vtablepFun = (Fun)pVtab[2][3];cout<<pFun<<endl;return0;}。

相关文档
最新文档