怎样获得虚函数的指针

本文介绍了一个使用C++实现的获取类中虚函数地址的方法,并通过实例演示了如何通过偏移量调用不同的虚函数。

先请看下面的简单的例子程序:

#include <iostream>

using namespace std;

 

class A

{

private:

         int a;

         int b;

public:

         virtual void vfun0()

         {

                   cout << "A::vfun0" << endl;

         }

 

         virtual void vfun1()

         {

                   cout << "A::vfun1" << endl;

         }

};

 

// 定义一个函数指针类型fun,此类函数没有参数,返回类型为void

typedef void (*fun)();

 

fun getVirtualFunction(A* obj, unsigned long offset)

{

         // 1. obj就是类A的对象地址,从前面的a中我们知道,vfptr总是在第一位的,因此obj其实也就是vfptr的开始;

         // 2. 在32-bit的操作系统中,地址空间也是32-bit的,我们知道long是4Bytes,因此(unsigned long*)obj就是vfptr

         // 指针(即包括obj及其后面3个Bytes的内容,unsigned long*从本质上说,就是限定包括obj及其后面3个Bytes的

        // 内容作为vfptr指针);

        // 3. *(unsigned long*)obj,就是vfptr指针中的内容,其中前4Bytes也就是virtual table的起始地址;

        // 4. (unsigned long *)(*(unsigned long*)obj)取得virtual table的4Bytes地址,也就是虚函数表中第一项,它也

        // 是一个指针,这个指针指向第一个虚函数的地址,也就是说该指针的内容为第一个虚函数的指针;如果offset = 1,

        // 那么(unsigned long *)(*(unsigned long*)obj) + offset就是虚函数表中第二项,它是一个指向第二个虚函数地址的指

        // 针,依此类推;

         unsigned long* vtbl = (unsigned long *)(*(unsigned long*)obj) + offset;

         // 5. 承4,如果vtbl是虚函数表中第一项,那么*(vtbl)就是第一个虚函数的指针,通过(fun)转化成为一个无参数,返回

        // 值类型为void的函数指针,一次类推。

         fun p = (fun) *(vtbl);

         return p;

}

 

int main(void)

{

         A a;

         cout << "Size of class A = " << sizeof(a) << endl;

         cout << &a << endl;

 

         getVirtualFunction(&a, 0)();

         getVirtualFunction(&a, 1)();

 

         return 0;

}

运行结果:

 

我们可以看到,通过

         getVirtualFunction(&a, 0)();

         getVirtualFunction(&a, 1)();

成功地调用了class A中的两个虚函数vfun0和vfun1。getVirtualFunction(&a, 0)和getVirtualFunction(&a, 1)分别就是vfun0和vfun1的函数指针。

 

 

C++中的虚函数指针机制是实现多态的核心技术之一。它通过虚函数表(vtable)和虚函数指针(vptr)来完成动态绑定,从而允许在运行时根据对象的实际类型调用相应的函数。 每个具有虚函数的类都会生成一个虚函数表,该表中包含了所有虚函数的地址。当一个类被实例化为对象时,该对象会包含一个指向其类的虚函数表指针(通常称为vptr)。这个指针使得程序能够在运行时确定应该调用哪个具体的虚函数[^1]。 具体来说,当定义了一个带有虚函数的类的对象时,编译器会在对象的内存布局中自动插入一个隐藏的数据成员——即虚函数指针(vptr)。此指针指向了与该对象所属类相关联的虚函数表。每当创建新的对象或者派生类的对象时,这些对象内部的vptr会被正确地初始化以指向它们各自类的vtable[^1]。 对于虚函数调用,比如通过基类指针或引用访问虚函数的情况,在运行时系统首先利用对象内的vptr找到对应的虚函数表,然后从虚函数表中查找所需的虚函数地址,并最终执行该函数。这种间接寻址的方式带来了轻微的时间开销:相较于普通函数调用,一次虚函数调用需要两次额外的取值操作,第一次获取vptr本身的值,第二次则是依据vptr得到虚函数的具体地址[^1]。 值得注意的是,如果尝试访问一个未初始化的虚函数指针,大多数现代编译器并不会简单地放置空指针,而是指向一个通用错误处理函数,该函数可以输出错误信息并终止应用程序,以此提供比单纯的段错误更友好的诊断结果[^3]。 此外,C++程序中发生函数调用动态绑定的情形包括使用动态库的情况下、显式使用dlsym等操作系统特定函数以及通过虚函数分发表进行虚成员函数查找时[^2]。 ```cpp #include <iostream> using namespace std; class Base { public: virtual void show() { cout << "Base class show function" << endl; } }; class Derived : public Base { public: void show() override { cout << "Derived class show function" << endl; } }; int main() { Base base; Derived derived; Base* ptr = &base; ptr->show(); // Calls Base::show() ptr = &derived; ptr->show(); // Calls Derived::show() return 0; } ``` 上述示例代码演示了一个简单的虚函数应用案例。`Base` 类有一个虚函数 `show()`,而 `Derived` 类重写了这个函数。在主函数中,我们声明了两个对象 `base` 和 `derived`,并通过基类指针 `ptr` 调用了 `show()` 函数。由于 `show()` 是虚函数,因此即使 `ptr` 指向的是 `Derived` 对象,也能正确地调用到 `Derived` 版本的 `show()` 函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值