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()的,因此便能完成析构函数的正确调用。