c++ vtable 虚函数表

1.作用:

用于有虚函数对象的指针,其在运行期间决定实际应该执行的函数的地址


2.内存布局:

内存的开头位置(64位),即内存开头8字节内容为vtable的的地址值

而vtable顺序存放函数地址值(64位顺序数组)


3.代码调用:

#include <stdio.h>

#include <iostream>

class P { 

 public:
    virtual void test() {std::cout << "test " << std::endl;}
    virtual void testa() {std::cout << "testa " << std::endl;}

 private:
    int a;
    
};

class S : public P { 
 public:
    void test() {std::cout << "test s" << std::endl;}
    void testc() {std::cout << "test s c" << std::endl;} 

 private:
    int c;
};
int main() {
    P *p = new S();
    void *ptr = (void*)(*((long*)p));
    printf("%x\n", ptr);

    void (*fun)(void) = (void(*)(void))(*((long*)ptr));
    printf("%x\n", fun);
    (fun)();
    delete p;

    return 0;
}
void *ptr = (void*)(*((long*)p)); 获取到vtable的地址

void (*fun)(void) = (void(*)(void))(*((long*)ptr));获取函数指针


注:

1.只有拥有virtual的类的对象才会有vtable,对于非virtual的函数,在编译器就能直接指定调用,而无需额外操作来得到


case:

析构函数为什么一般需要virtual;

假设P是S的父类,那么默认情况下S的析构函数生成的代码会自动包含调用父类析构函数的代码,

这也就能说明当使用S s;等本地变量的时候,其是能够处理完自己的析构函数再自动调用父类的析构函数(构造函数则相反),即代码已经在子类的析构函数中,

但是当用P *p = new S();的时候,如果p的析构函数不是virtual的,那么会只有析构~P函数,这是因为,~P()并不是虚函数,那么不会在vtable中,其决定析构函数的调用,

在编译期间便根据P的类型,直接只调用~P的

那么当~P为虚函数的时候,~P是作为vtable的函数的,当其指向S()的时候,其~P()的函数实际是指向~S()的,因此便能完成析构函数的正确调用。


发布了140 篇原创文章 · 获赞 28 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/qq_16097611/article/details/78932019
今日推荐