动态联编的原理 (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();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值