#include <stdio.h>
#include <stdlib.h>
#include <string.h>
class A{
protected:
virtual void fun(){
printf("class:a function:fun\n");
}
virtual void fun2(){
printf("class:a function:fun2\n");
}
};
class B:public A{
protected:
void fun(){
printf("class:b function:fun\n");
}
void fun2(){
printf("class:b function:fun2\n");
}
};
int main(){
//输入和输出重定向
freopen("in.txt","r", stdin);
freopen("out.txt", "w", stdout);
void (*fun)(A*);
A* pa = new B();
long fp;
memcpy(&fp, pa, 4);
memcpy(&fun, reinterpret_cast<long*>(fp), 4);
fun(pa);
memcpy(&fun, reinterpret_cast<long*>(fp)+1, 4);
fun(pa);
delete pa;
return 0;
}
输出结果如下:
class:b function:fun
class:b function:fun2
百度百科的解释:
void (*fun)(A*); 这段定义了一个
函数指针名字叫做fun,而且有一个A*类型的参数,这个函数指针待会儿用来保存从vtbl里取出的函数地址
A* p=new B(); new B是向内存(内存分5个区:全局名字空间,自由存储区,寄存器,代码空间,栈)自由存储区申请一个内存单元的地址然后隐式地保存在一个
指针中.然后把这个地址赋值给A类型的指针P.
.
long fp; 这个long类型的
变量待会儿用来保存vptr的值
memcpy(&fp,pa,4); 前面说了,他们的实例对象里只有vptr
指针,所以我们就放心大胆地把pa所指的4bytes内存里的东西复制到fp中,所以复制出来的4bytes内容就是vptr的值,即vtbl的地址
现在有了vtbl的地址了,那么我们现在就取出vtbl第一个slot里的内容
memcpy(&fun, reinterpret_cast<long*>(fp), 4); 取出vtbl第一个slot里的内容,并存放在
函数指针fun里。需要注意的是fp里面是vtbl的地址,但fp不是
指针,所以我们要把它先转变成指针类型
fun(pa); 这里就调用了刚才取出的函数地址里的函数,也就是调用了B::fun()这个函数,也许你发现了为什么会有参数p,其实类
成员函数调用时,会有个this
指针,这个p就是那个this指针,只是在一般的调用中
编译器自动帮你处理了而已,而在这里则需要自己处理
分享到:
相关推荐
然而,这里有一个陷阱:如果通过子类对象直接调用父类的虚函数,编译器会认为是在调用子类自身的实现,即使子类没有覆盖这个虚函数。这就是“子类对象不能调用父类中的虚函数”的含义。例如,假设我们有以下代码: ...
1. **动态类型与静态类型不匹配**:在析构函数中调用虚函数时,C++使用的是静态绑定,即根据对象的静态类型(即编译时已知的类型)来决定调用哪个函数版本,而不是动态类型(即运行时的实际类型)。因此,即使你期望...
3. **虚函数调用**:虚函数允许在派生类中重写基类的函数,从而实现多态性。通过基类指针或引用调用时,会根据实际指向的对象类型动态决定调用哪个版本的函数。 4. **构造函数与析构函数调用**:构造函数用于初始化...
本文旨在探讨C++中的虚函数调用机制,通过分析一个简单的示例程序,解释虚函数如何在运行时被正确地调用,并深入剖析这一过程背后的原理。 #### C++中的虚函数调用机制 C++中的虚函数调用机制主要依赖于虚拟表...
在C++中,成员函数的指针是个...但对成员函数来说,常规类型转换是通不过编译的,调用的时候也必须采用特殊的语法。C++专门为成员指针准备了三个运算符: "::*"用于指针的声明,而"->*"和".*"用来调用指针指向的函数。
当通过基类的指针或者引用调用一个虚函数时,编译器会首先通过对象指针找到对应的虚函数表,然后根据调用的虚函数名在虚函数表中查找对应的函数地址,进而调用正确的函数实现。 #### 三、虚函数表的结构与实现 虚...
1. **避免内存泄漏**:如果基类指针指向派生类对象,并且基类的析构函数不是虚函数,那么通过基类指针删除派生类对象时,只会调用基类的析构函数。这样就会导致派生类中额外分配的资源没有得到释放,从而引发内存...
通过虚函数,可以在基类中定义一个接口,并由派生类来具体实现该接口的功能,进而允许在运行时根据对象的实际类型动态调用正确的函数实现。本文将基于提供的示例代码,深入探讨虚函数的内部实现机制,特别是虚函数表...
Delphi子类调用祖父类的虚函数.mht
在上面的代码中,虽然没有直接展示虚函数表,但其工作原理如下:`base_class`、`drived_class1`和`drived_class2`每个类都有自己的vtable,`pbc`指向不同对象时,调用虚函数实际上是在访问相应的vtable并执行相应的...
- 虚函数机制依赖于运行时类型识别(RTTI),这意味着在运行时才能确定实际调用的是哪个类的函数。 - 构造函数的主要任务是在对象创建时对其进行初始化,此时对象还未完全构建完成,其确切类型未知,因此无法有效地...
这样,当通过基类指针调用虚函数时,实际上调用的是派生类中对应的函数实现,即使是在运行时才确定对象的实际类型。 #### 虚函数表的实现 在C++标准中,编译器必须确保虚函数表的指针位于对象实例的最前面位置,以...
因此,如果在构造函数中调用虚函数,程序可能会错误地认为对象是基类类型,而不是实际的派生类类型,从而导致错误的结果。 同样,在析构过程中,一旦派生类的析构函数开始执行,对象的派生类数据成员就被认为是...
在C++中,虚函数是实现多态性的重要机制,多态是指使用父类型的指针或引用能够调用子类的成员函数,从而允许不同类型的对象以统一的方式进行处理。这种特性使得C++具备了泛型编程的能力,即用不变的代码解决可变的...
构造函数不能是虚函数,因为构造函数在对象创建时被调用,而此时虚函数表还未初始化。 7. **C++11的final关键字**: 从C++11开始,可以使用`final`关键字阻止子类进一步覆盖虚函数: ```cpp class Base { ...
在C++编程中,类之间的相互调用是一个常见的设计模式,尤其在实现复杂系统或对象间的协作时。这种设计允许类A访问类B的成员,同时类B也可以访问类A的成员,形成一种循环依赖的关系。然而,这种关系需要谨慎处理,以...
虚函数使得可以通过基类指针或引用来调用派生类中的重定义函数,从而实现动态绑定。 首先,当一个类声明了一个或多个虚函数,系统就会为这个类创建一个虚函数表(Virtual Table,简称VTBL)。这个虚表包含类的所有...
在一个类的成员函数中,可以直接调用本类的其他成员函数,也可以通过对象或指针间接调用其他类的成员函数。例如,我们有`ClassA`和`ClassB`两个类,`ClassA`中有一个成员函数`funcA()`,`ClassB`中有一个成员函数`...