动态联编的原理 (C++)

  1. 只有一个基类
#include <iostream>
#include <iomanip>
using namespace std;

int global = 0;

/*
 * 考察动态联编的实现原理
 * 环境: VS2017 
 * 定义A类,包含一个int成员,两个虚函数
 * 
*/
class A {
    
    
	int i = ++global;
	public:
		virtual void show1() {
    
    
			cout<<"hello world"<<endl;
		}
		virtual void show2() {
    
    
			cout<<"hi "<<endl;
		}
};

int main () {
    
    
	A a, b;

	// A类型的大小为8个字节,而不是4个字节
	// 原因是,因为A类包含有虚函数,因此隐含有一个虚指针(vptr),该指针指向虚函数表
	cout<<"sizeof(A) = "<<sizeof(A)<<endl; // 输出: sizeof(A) = 8

	// 下面输出对象a的在内存中的内容
	
	// 将a的地址转换成unsigned long*,即将a中的内容看成是unsigned long类型的数组
	// 依次查看每四个字节中的内容
	unsigned long* ptr = reinterpret_cast<unsigned long*>(&a);
	cout<<"Object [a]:"<<endl;
	cout<<"           ptr[0] = "<<ptr[0]<<endl;
	cout<<"           ptr[1] = "<<ptr[1]<<endl;

	// 将b的内容解释成unsigned long数组
	ptr = reinterpret_cast<unsigned long*>(&b);
	cout<<"Object [b]:"<<endl;
	cout<<"           ptr[0] = "<<ptr[0]<<endl;
	cout<<"           ptr[1] = "<<ptr[1]<<endl;

	// 通过查看上面对象a和对象b在内存中的内容可以发现:
	//    1. 对象a和对象b前4个字节的内容是相同的。
	//    2. 对象a和对象b后4个字节的内容分别是1和2,即成员变量i的值
	// 因此,在含有虚函数的类中,在首地址中会有一个虚指针,该指针中的内容存放着该类的虚函数表在内存中的地址
	// 根据不同对象的虚指针值相同这一事实,可以知道每个包含虚函数的类,其虚函数表是唯一的(存放在常量区)

	// 下面来查看虚函数表的内容

	// 获取虚函数表的地址
	unsigned long vtable_addr = ptr[0];

	// 转换成指针,解析虚函数表的内容
	unsigned long* vtable = reinterpret_cast<unsigned long*>(vtable_addr);

	// 虚函数表第一项的内容,即虚函数: void show1(); 在内存中的入口地址
	cout<<"vtable[0] = 0x"<<hex<<vtable[0]<<endl;
	// 虚函数表第二项的内容,即虚函数: void show1(); 在内存中的入口地址
	cout<<"vtable[1] = 0x"<<hex<<vtable[1]<<endl;

	// 声明一个函数指针类型
	using pFunc = void(*)();

	// 将虚函数表的第一项转换成函数指针
	pFunc pf = reinterpret_cast<pFunc>(vtable[0]);
	pf();

	// 将虚函数表的第二项转换成函数指针
	pf = reinterpret_cast<pFunc>(vtable[1]);
	pf();
}
  1. 存在派生类
#include <iostream>
#include <iomanip>
#include <typeinfo>
using namespace std;

int global = 0;

/*
 * 考察动态联编的实现原理
 * 环境: VS2017 
 * 定义A类,包含一个int成员,两个虚函数
 * 
*/
class A {
    
    
	int i = ++global;
	public:
		virtual void show1() {
    
    
			cout<<"A::show1()"<<endl;
		}
		virtual void show2() {
    
    
			cout<<"A::show2()"<<endl;
		}
};

class B : public A {
    
    
	public:
		void show2() override {
    
    
			cout<<"B::show2()"<<endl;
		}
};

template <typename T> void dynamic();

int main() {
    
    
	cout<<"class A"<<endl;
	dynamic<A>();
	cout<<"-------------"<<endl;
	cout<<"class B"<<endl;
	dynamic<B>();

	// 通过上面的输出可以知道:
	//     1. 每个包含有虚函数的类都有一个虚函数表
	// 	   2. 根据vtable第一项相同,而第二项不同,结合类的定义中,子类B覆盖了父类A中的show2函数的定义,可以知道
	//            当子类中重写了父类的虚函数时,被重写的虚函数在虚函数表中的内容(入口地址)将发生改变(与父类的虚函数表比较),
	//        而未被重写的虚函数在虚函数表中的内容不变
	//        


}




template <typename T>
void dynamic () {
    
    
	T a, b;

	// 大小为8个字节,而不是4个字节
	// 原因是,因为类包含有虚函数,因此隐含有一个虚指针(vptr),该指针指向虚函数表
	cout<<"sizeof("<<typeid(T).name()<<") = "<<sizeof(T)<<endl; // 输出: sizeof(A) = 8

	// 下面输出对象a的在内存中的内容
	
	// 将a的地址转换成unsigned long*,即将a中的内容看成是unsigned long类型的数组
	// 依次查看每四个字节中的内容
	unsigned long* ptr = reinterpret_cast<unsigned long*>(&a);
	cout<<"Object [a]:"<<endl;
	cout<<"           ptr[0] = "<<ptr[0]<<endl;
	cout<<"           ptr[1] = "<<ptr[1]<<endl;

	// 将b的内容解释成unsigned long数组
	ptr = reinterpret_cast<unsigned long*>(&b);
	cout<<"Object [b]:"<<endl;
	cout<<"           ptr[0] = "<<ptr[0]<<endl;
	cout<<"           ptr[1] = "<<ptr[1]<<endl;

	// 通过查看上面对象a和对象b在内存中的内容可以发现:
	//    1. 对象a和对象b前4个字节的内容是相同的。
	//    2. 对象a和对象b后4个字节的内容分别是1和2,即成员变量i的值
	// 因此,在含有虚函数的类中,在首地址中会有一个虚指针,该指针中的内容存放着该类的虚函数表在内存中的地址
	// 根据不同对象的虚指针值相同这一事实,可以知道每个包含虚函数的类,其虚函数表是唯一的(存放在常量区)

	// 下面来查看虚函数表的内容

	// 获取虚函数表的地址
	unsigned long vtable_addr = ptr[0];

	// 转换成指针,解析虚函数表的内容
	unsigned long* vtable = reinterpret_cast<unsigned long*>(vtable_addr);

	// 虚函数表第一项的内容,即虚函数: void show1(); 在内存中的入口地址
	cout<<"vtable[0] = 0x"<<hex<<vtable[0]<<endl;
	// 虚函数表第二项的内容,即虚函数: void show1(); 在内存中的入口地址
	cout<<"vtable[1] = 0x"<<hex<<vtable[1]<<endl;

	// 声明一个函数指针类型
	using pFunc = void(*)();

	// 将虚函数表的第一项转换成函数指针
	pFunc pf = reinterpret_cast<pFunc>(vtable[0]);
	pf();

	// 将虚函数表的第二项转换成函数指针
	pf = reinterpret_cast<pFunc>(vtable[1]);
	pf();
}

猜你喜欢

转载自blog.csdn.net/weixin_40315481/article/details/108083773