c++ 虚函数表

c++多态通过虚函数实现。研究下如何通过虚函数表调用虚函数,顺便看下其原理。直接代码演示:

cpp 复制代码
void virFuc(void) {
	class Base {
	public:
		virtual void Test1() { // 父类虚函数1
			std::cout << __FUNCSIG__ << std::endl;
		}

		virtual void Test2() { // 父类虚函数2
			std::cout << __FUNCSIG__ << std::endl;
		}
	};

	class Sub :public Base {
	public:
		void Test1() override { // 子类覆盖父类虚函数
			std::cout << __FUNCSIG__ << std::endl;
		}

		virtual int Test3(string& str) { // 子类虚函数
			std::cout << __FUNCSIG__  << " " << str << std::endl;
			return str.size();
		}
	};

	cout << "======================================== 通过对象调用虚函数:" << endl;
	Base base;
	base.Test1();

	Base* sub = new Sub();
	sub->Test1();

	cout << "======================================== 下面演示利用虚函数表调用虚函数:" << endl;

	// 有虚函数的类,对于64位程序,其对象的头部8个字节存放一个指针(虚表指针vptr),该指针指向类的虚函数表(vtable, 虚函数表在编译时生成,属于类的静态数据)。
	// 上面Base类的虚函数表结构[Base::Test1, Base::Test2] ; 而Sub类的虚函数表结构[Sub::Test1, Base::Test2, Sub::Test3] 。
	// 注意, Sub类虚函数表中,基类的Test1函数指针被替换为了自身的Test1函数指针.
	// 调用虚函数,实际是从虚函数表中找到该函数指针再调用。即根据对象的vptr找到虚表,再偏移量定位函数地址,这是多态的调用机制。
	// 看看如何拿到虚函数表里的函数指针
	
	Base* b = new Sub();
	void* objPtr = reinterpret_cast<void*>(b);

	// 先从对象中得到虚函数表指针
	// 对象首地址存储的虚指针vptr,解引用得到虚表地址
	void** vtablePtr = *(void***)objPtr;

	// 从虚表中获取第一个虚函数指针
	typedef void (*FuncPtr1)(Base*);
	FuncPtr1 func1 = (FuncPtr1)vtablePtr[0];
	func1(b);

	// 同理调用Test2
	typedef void (*FuncPtr2)(Base*);
	FuncPtr2 func2 = (FuncPtr2)vtablePtr[1];
	func2(b);

	// 同理调用Test3
	typedef int (*FuncPtr3)(Base*, string&);
	FuncPtr3 func3 = (FuncPtr3)vtablePtr[2];
	string str = "我的战斗力6000,他起码有10000以上!";
	func3(b, str);
}

打印:

ok.