C++ VTABLE

黎 浩然/ 10 5 月, 2022/ C/C++, 计算机/COMPUTER/ 0 comments

/**
* @file vptr1.cpp
* @brief C++虚函数vptr和vtable
* 编译:g++ -g -o test test.cpp -std=c++11 * @author Ernest
* @version v1
* @date 2019-07-20
*/
#include <iostream>
#include <cstdio>
using namespace std;

/*
 * @brief 函数指针 
 */
typedef void (*Fun)();

/*
 * @brief 基类 
 */
class Base
{
public:
    Base(){};
    virtual void fun1() {
        cout << "Base::fun1()" << endl;
    }
    virtual void fun2() {
        cout << "Base::fun2()" << endl;
    }
    virtual void fun3(){}
    ~Base(){};
};

/**
 * @brief 派生类 
 */
class Derived: public Base
{
public:
    Derived(){};
    void fun1() {
        cout << "Derived::fun1()" << endl;
    }
    void fun2() {
        cout << "DerivedClass::fun2()" << endl;
    }
    ~Derived(){};
};

/**
* @brief 获取vptr地址与func地址,vptr指向的是一块内存存放的是虚函数地址,也就是我们
所说的虚表
 *
 * @param obj
 * @param offset
 *
 * @return
 */
Fun getAddr(void* obj,unsigned int offset){ 
    cout<<"======================="<<endl;
    void* vptr_addr = (void *)*(unsigned long *)obj; //64位操作系统,占8字节
    printf("vptr_addr:%p\\n",vptr_addr);
    /**
     * @brief 通过vptr指针访问virtual table
     * 因为虚表中每个元素(虚函数指针)在64位编译器下是8个字节 
     * 因此通过*(unsigned long *)vptr_addr取出前8字节, 
     * 后面加上偏移量就是每个函数的地址!
     */
    void* func_addr = (void *)*((unsigned long *)vptr_addr+offset);
    printf("func_addr:%p\\n",func_addr);
    return (Fun)func_addr;
}

int main(void) {
    Base base;
    Derived derived;    
    Base *bptd = new Derived(); // 基类指针指向派生类实例 
    Base &brtb = base; // 基类引用指向基类实例 
    Base &brtd = derived; // 基类引用指向派生类实例
    cout<<"基类对象直接调用"<<endl;
    base.fun1(); 
    cout<<"基类引用调用基类实例"<<endl; 
    brtb.fun1(); 
    cout<<"基类指针指向派生类实例并调用虚函数"<<endl; 
    bptd->fun1(); 
    cout<<"基类引用派生类实例并调用虚函数"<<endl; 
    brtd.fun1();
    // 手动查找vptr 和 vtable 
    Fun f1 = getAddr(bptd, 0); (*f1)();
    Fun f2 = getAddr(bptd, 1); (*f2)();
    delete bptd;
    return 0;
}

如上图所示,fun1和fun2被重载过,所以对应的虚函数表指向不同的位置(函数)!

Share this Post

Leave a Comment

您的邮箱地址不会被公开。 必填项已用 * 标注

*
*