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++中,对于类中的成员函数,可以将其声明为纯函数和虚函数,也可以同时声明为纯虚函数和虚函数。

纯虚函数是指在基类中声
明的函数,但没有给出函数实现,需要在派生类中定义函数实现。


函数和纯虚函数都可以使用override关键字来在派生类中重定义,从
而实现更加灵活的代码结构。

虚函数的实现原理

虚函数的实现原理

虚函数的实现原理虚函数是C++中的一种特殊函数,它允许派生类重写基类的同名函数,并根据对象的实际类型调用相应的函数。

虚函数的实现原理涉及到虚函数表(vtable)和虚函数指针(vpointer)两个重要的概念。

在C++中,每个包含虚函数的类都会生成一个与之对应的虚函数表(vtable)。

虚函数表是一个以函数指针为元素的数组,用于存储类的虚函数地址。

虚函数表中的每个元素都对应着类的一个虚函数,其中存储着该虚函数的地址。

虚函数表通常位于类的内存布局最前面的位置。

当一个类被定义为包含虚函数时,编译器会自动生成一个隐藏的虚函数指针(vpointer)并将它添加到类的内存布局中。

这个虚函数指针被添加到每一个类的对象中,并指向该对象对应的虚函数表。

通过虚函数指针,程序能够在运行时根据对象的实际类型找到正确的虚函数表,并调用相应的虚函数。

当派生类重写基类的虚函数时,它会生成一个新的函数地址并将其存储到自己对应的虚函数表中。

在派生类的虚函数表中,只有被重写的虚函数所对应的表项会被替换为新的函数地址,其它虚函数的表项仍然指向基类的虚函数地址。

这样,当通过派生类的对象调用虚函数时,程序会根据对象的实际类型找到对应的虚函数表,并调用正确的虚函数。

虚函数的实现原理可以通过以下示例代码进行说明:cpp#include <iostream>class Base {public:virtual void print() {std::cout << "Base::print()" << std::endl;}};class Derived : public Base {public:void print() override {std::cout << "Derived::print()" << std::endl;}};int main() {Base* base = new Derived();base->print(); 输出"Derived::print()"delete base;return 0;}在上述代码中,Base类包含一个虚函数print(),而Derived类重写了这个虚函数。

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形象地看成一个函数指针数组,这个数组的每个元素存放的就是虚函数的地址。

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.

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就是⼀个普通的函数。

c语言虚函数

c语言虚函数一、什么是虚函数在C++中,虚函数是指在基类中被声明为虚函数的成员函数,在派生类中重新定义后,会根据对象的实际类型来选择调用哪个版本的函数。

这种机制称为动态绑定或运行时多态。

二、C语言中是否支持虚函数C语言并不直接支持虚函数,因为它没有面向对象的特性。

但是,我们可以通过结构体和函数指针模拟出类和虚函数的概念。

三、如何实现虚函数1. 定义一个基类结构体,并在其中定义一个指向成员函数的指针作为虚函数。

struct Base {int (*fun)(struct Base *self);};2. 定义一个派生类结构体,并在其中定义一个指向成员函数的指针作为重写后的虚函数。

struct Derived {struct Base base;int (*fun)(struct Derived *self);};3. 实现基类和派生类各自对应的成员函数。

int base_fun(struct Base *self) {printf("Base fun\n");return 0;}int derived_fun(struct Derived *self) {printf("Derived fun\n");return 0;}4. 在程序中创建基类对象和派生类对象,并分别调用它们各自对应的成员函数。

int main() {struct Base base = {base_fun};struct Derived derived = {{base_fun}, derived_fun};base.fun(&base);derived.base.fun(&derived);derived.fun(&derived);return 0;}四、虚函数的优缺点1. 优点虚函数可以实现多态,使得不同类型的对象在调用同一个函数时,可以根据实际类型来选择调用哪个版本的函数。

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语言中,虚函数的定义需要使用函数指针和结构体实现。

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

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

下面是一个虚函数的定义示例: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语言中实现类似于面向对象的继承和多态的功能。

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

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

虚函数联系到多态,多态联系到继承。

所以本文中都是在继承层次上做文章。

没了继承,什么都没得谈。

下面是对C++的虚函数这玩意儿的理解。

一,什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你就应该从这里开始)简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。

虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略。

下面来看一段简单的代码class A{public:void print(){ cout<<”This is A”<<endl;}};class B:public A{public:void print(){ cout<<”This is B”<<endl;}};int main(){ //为了在以后便于区分,我这段main()代码叫做main1A a;B b;a.print();b.print();}通过class A和class B的print()这个接口,可以看出这两个class因个体的差异而采用了不同的策略,输出的结果也是我们预料中的,分别是This is A和This is B。

但这是否真正做到了多态性呢?No,多态还有个关键之处就是一切用指向基类的指针或引用来操作对象。

那现在就把main()处的代码改一改。

int main(){ //main2A a;B b;A* p1=&a;A* p2=&b;p1->print();p2->print();}运行一下看看结果,哟呵,蓦然回首,结果却是两个This is A。

问题来了,p2明明指向的是class B的对象但却是调用的class A的print()函数,这不是我们所期望的结果,那么解决这个问题就需要用到虚函数class A{public:virtual void print(){ cout<<”This is A”<<endl;} //现在成了虚函数了};class B:public A{public:void print(){ cout<<”This is B”<<endl;} //这里需要在前面加上关键字virtual 吗?};毫无疑问,class A的成员函数print()已经成了虚函数,那么class B的print()成了虚函数了吗?回答是Yes,我们只需在把基类的成员函数设为virtual,其派生类的相应的函数也会自动变为虚函数。

所以,class B的print()也成了虚函数。

那么对于在派生类的相应函数前是否需要用virtual关键字修饰,那就是你自己的问题了。

现在重新运行main2的代码,这样输出的结果就是This is A和This is B了。

现在来消化一下,我作个简单的总结,指向基类的指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。

二,虚函数是如何做到的(如果你没有看过《Inside The C++ Object Model》这本书,但又急切想知道,那你就应该从这里开始)虚函数是如何做到因对象的不同而调用其相应的函数的呢?现在我们就来剖析虚函数。

我们先定义两个类class A{ //虚函数示例代码public:virtual void fun(){cout<<1<<endl;}virtual void fun2(){cout<<2<<endl;}};class B:public A{public:void fun(){cout<<3<<endl;}void fun2(){cout<<4<<endl;}};由于这两个类中有虚函数存在,所以编译器就会为他们两个分别插入一段你不知道的数据,并为他们分别创建一个表。

那段数据叫做vptr指针,指向那个表。

那个表叫做vtbl,每个类都有自己的vtbl,vtbl的作用就是保存自己类中虚函数的地址,我们可以把vtbl形象地看成一个数组,这个数组的每个元素存放的就是虚函数的地址,请看图通过上图,可以看到这两个vtbl分别为class A和class B服务。

现在有了这个模型之后,我们来分析下面的代码A *p=new A;p->fun();毫无疑问,调用了A::fun(),但是A::fun()是如何被调用的呢?它像普通函数那样直接跳转到函数的代码处吗?No,其实是这样的,首先是取出vptr的值,这个值就是vtbl的地址,再根据这个值来到vtbl这里,由于调用的函数A::fun()是第一个虚函数,所以取出vtbl第一个slot里的值,这个值就是A::fun()的地址了,最后调用这个函数。

现在我们可以看出来了,只要vptr不同,指向的vtbl就不同,而不同的vtbl里装着对应类的虚函数地址,所以这样虚函数就可以完成它的任务。

而对于class A和class B来说,他们的vptr指针存放在何处呢?其实这个指针就放在他们各自的实例对象里。

由于class A和class B都没有数据成员,所以他们的实例对象里就只有一个vptr指针。

通过上面的分析,现在我们来实作一段代码,来描述这个带有虚函数的类的简单模型。

#include<iostream>using namespace std;//将上面“虚函数示例代码”添加在这里int main(){void (*fun)(A*);A *p=new B;long lVptrAddr;memcpy(&lVptrAddr,p,4);memcpy(&fun,reinterpret_cast<long*>(lVptrAddr),4);fun(p);delete p;system("pause");}用VC或Dev-C++编译运行一下,看看结果是不是输出3,如果不是,那么太阳明天肯定是从西边出来。

现在一步一步开始分析void (*fun)(A*); 这段定义了一个函数指针名字叫做fun,而且有一个A*类型的参数,这个函数指针待会儿用来保存从vtbl里取出的函数地址A* p=new B; 这个我不太了解,算了,不解释这个了long lVptrAddr; 这个long类型的变量待会儿用来保存vptr的值memcpy(&lVptrAddr,p,4); 前面说了,他们的实例对象里只有vptr指针,所以我们就放心大胆地把p所指的4bytes内存里的东西复制到lVptrAddr中,所以复制出来的4bytes内容就是vptr的值,即vtbl的地址现在有了vtbl的地址了,那么我们现在就取出vtbl第一个slot里的内容memcpy(&fun,reinterpret_cast<long*>(lVptrAddr),4); 取出vtbl第一个slot里的内容,并存放在函数指针fun里。

需要注意的是lVptrAddr里面是vtbl的地址,但lVptrAddr不是指针,所以我们要把它先转变成指针类型fun(p); 这里就调用了刚才取出的函数地址里的函数,也就是调用了B::fun()这个函数,也许你发现了为什么会有参数p,其实类成员函数调用时,会有个this指针,这个p就是那个this指针,只是在一般的调用中编译器自动帮你处理了而已,而在这里则需要自己处理。

delete p;和system("pause"); 这个我不太了解,算了,不解释这个了如果调用B::fun2()怎么办?那就取出vtbl的第二个slot里的值就行了memcpy(&fun,reinterpret_cast<long*>(lVptrAddr+4),4); 为什么是加4呢?因为一个指针的长度是4bytes,所以加4。

或者memcpy(&fun,reinterpret_cast<long*>(lVptrAddr)+1,4); 这更符合数组的用法,因为lVptrAddr被转成了long*型别,所以+1就是往后移sizeof(long)的长度三,以一段代码开始#include<iostream>using namespace std;class A{ //虚函数示例代码2public:virtual void fun(){ cout<<"A::fun"<<endl;} virtual void fun2(){cout<<"A::fun2"<<endl;} };class B:public A{public:void fun(){ cout<<"B::fun"<<endl;}void fun2(){ cout<<"B::fun2"<<endl;}}; //end//虚函数示例代码2int main(){void (A::*fun)(); //定义一个函数指针A *p=new B;fun=&A::fun;(p->*fun)();fun = &A::fun2;(p->*fun)();delete p;system("pause");}你能估算出输出结果吗?如果你估算出的结果是A::fun和A::fun2,呵呵,恭喜恭喜,你中圈套了。

其实真正的结果是B::fun和B::fun2,如果你想不通就接着往下看。

给个提示,&A::fun和&A::fun2是真正获得了虚函数的地址吗?首先我们回到第二部分,通过段实作代码,得到一个“通用”的获得虚函数地址的方法#include<iostream>using namespace std;//将上面“虚函数示例代码2”添加在这里void CallVirtualFun(void* pThis,int index=0){void (*funptr)(void*);long lVptrAddr;memcpy(&lVptrAddr,pThis,4);memcpy(&funptr,reinterpret_cast<long*>(lVptrAddr)+index,4);funptr(pThis); //调用}int main(){A* p=new B;CallVirtualFun(p); //调用虚函数p->fun()CallVirtualFun(p,1);//调用虚函数p->fun2()system("pause");}现在我们拥有一个“通用”的CallVirtualFun方法。

相关文档
最新文档