美文网首页
c++虚函数的虚指针和虚函数表

c++虚函数的虚指针和虚函数表

作者: 幸村的野望 | 来源:发表于2020-11-09 19:23 被阅读0次

    c++为了实现对象在运行时的动态调用函数使用了一种虚函数(vtable)的技术。
    虚函数表是编译器在编译阶段为类分配的静态数组,每一个使用了虚函数的类都有自己的虚函数表,表中的存放的都是函数指针,指向该类访问的虚函数。
    同时,编译器还是添加一个指向vtable的指针,称之为vptr,vptr在创建实例时自动设置。vptr的设定和重置都由每个类的构造、析构和拷贝赋值运算符自动完成。

    #include <iostream>
    using namespace std;
    
    typedef void (*Fun)();
    class Base {
    public:
      Base() {}
      virtual void fun1() { cout << "Base : fun1()" << endl; }
      virtual void fun2() { cout << "Base : fun2()" << endl; }
      virtual void fun3() { cout << "Base : fun3()" << endl; }
      ~Base() {}
    };
    
    /**
     * @brief
     * 获取vptr地址与func地址,vptr指向的是一块内存,这块内存存放的是虚函数地址,这块内存就是我们所说的虚表
     *
     */
    class Derived : public Base {
    public:
      Derived() {}
      void fun1() { cout << "Derived : fun1()" << endl; }
      void fun2() { cout << "Derived : fun2()" << endl; }
      void fun3() { cout << "Derived : fun3()" << endl; }
      void fun4() { cout << "Derived : fnu4()" << endl; }
      ~Derived() {}
    };
    
    Fun getAddr(void *obj, unsigned int offset) {
      cout << "====================" << endl;
      void *vptr_addr =
          (void *)*(unsigned long *)obj; // 64位操作系统,占8字节,通过*(unsigned
                                         // long *)obj取出前8字节,即vptr指针
      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 ptr;
      Derived d;
      Base *pt = new Derived();
      Base &pp = ptr;
      Base &p = d;
      cout << "基类对象直接调用" << endl;
      ptr.fun1();
      cout << "基类引用指向基类实例" << endl;
      pp.fun1();
      cout << "基类指针指向派生类实例并调用虚函数" << endl;
      pt->fun1();
      cout << "基类引用指向派生类实例并调用虚函数" << endl;
      p.fun1();
    
      /* 手动查找vptr和vtable */
      Fun f1 = getAddr(pt, 0);
      (*f1)();
      Fun f2 = getAddr(pt, 1);
      (*f2)();
      delete pt;
      Base *pt1 = new Derived(); // 基类指针指向派生类实例
      cout << "基类指针指向派生类实例并调用虚函数" << endl;
      pt1->fun1();
      return 0;
    }
    

    运行结果是:

    基类对象直接调用
    Base : fun1()
    基类引用指向基类实例
    Base : fun1()
    基类指针指向派生类实例并调用虚函数
    Derived : fun1()
    基类引用指向派生类实例并调用虚函数
    Derived : fun1()
    ====================
    vptr_addr:0x401218
    func_addr:0x400f50
    Derived : fun1()
    ====================
    vptr_addr:0x401218
    func_addr:0x400f90
    Derived : fun2()
    基类指针指向派生类实例并调用虚函数
    Derived : fun1()
    

    可以看到c++中实现动态多态就是使用的虚函数。

    相关文章

      网友评论

          本文标题:c++虚函数的虚指针和虚函数表

          本文链接:https://www.haomeiwen.com/subject/zgzlbktx.html