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++中实现动态多态就是使用的虚函数。
网友评论