c++ 虚函数表, RTTI, dynamic_cast的探索

本文深入探讨了C++中虚函数的实现机制,包括虚函数表(vtable)和__vfptr指针的作用。通过实例展示了类对象如何在内存中存储虚函数指针,并在运行时实现多态。此外,还介绍了RTTI(运行时类型识别)的概念,及其在typeid和dynamic_cast中的应用。最后,文章演示了如何在运行时动态访问和修改虚函数表。
摘要由CSDN通过智能技术生成

c++虚函数实现机制

c++多态指的是基类指针调用子类的方法. 多态的实现用的虚函数实现的,而虚函数是利用了虚函数表(vtable)实现的 。在每个类的实例化对象存在__vfptr指针指向了虚函数表,里面存在了该类所有的实际执行的虚函数的地址。

多态类对象中虚函数和虚函数表

class A
{
public:
	A(){}

	void Test(){}

	virtual void Print1(){}

	virtual void Print2(){}
};

class B : public A
{

public:
	B(){}

	virtual void Print2() override{}
};

class C
{
public:
	C() {}

	void Print1() {}
};

int main()
{
	A* a = new B();
	A* aa = new A();
	A* c = new B();
	C* d = new C();
	printf("A Print1 %p\n", &A::Print1);
	printf("A Print2 %p\n", &A::Print2);
	printf("B Print1 %p\n", &B::Print1);
	printf("B Print2 %p\n", &B::Print2);
	system("pause");
	return 0;
}

这里可以清晰看到ClassA和ClassB的虚函数地址区别,当然这里仅仅是类函数指针,多态真正的实现是Runtime,在类对象上体现。上面由class B创建的基类指针对象a的__vfptr分别指向Class A的Print1和Class B的Print2. 而由Class A创建的对象aa的__vfptr分别指向Class A的Print1和Print2.

从上面可以看出:

(1)类函数指针和对象函数指针不是同一个东西,两者地址不同的

(2)同一个类的所有实例化对象的虚函数表是同一个

(3)没有虚函数的类对象并没有虚函数表

 

虚函数表在类对象中的位置和虚函数表的妙用

在类对象的内存地址中, 多态类对象分配的内存首地址存着虚函数指针__vfptr,__vfptr指向虚函数表(目前探查是个数组)

class A
{
public:
	A(){}

	void Test(){}

	virtual void Print1() { printf("class A Print1\n"); }

	virtual void Print2() { printf("class A Print2\n"); }
};

class B : public A
{

public:
	B(){}

	virtual void Print2() override{ printf("class B Print2\n"); }
};

class C : public A
{

public:
	C() {}

	virtual void Print1() { printf("class C Print1\n"); }

	virtual void Print2() { printf("class C Print2\n"); }
};

using myfunc = void(*)(void);


class D : public C
{
public:
	D(){}

	void Print1() {}
};

int main()
{
	A* a = new B();
	int* p = (int*)a;
	printf("object adress is%p \n", p);
	int* vptr = (int*)p[0];
	printf("vfptr is %p \n", (int*)vptr);
	myfunc myptr1 = (myfunc)(vptr[0]);
	myptr1();
	myfunc myptr2 = (myfunc)(vptr[1]);
	myptr2();

	// runtime-change vfptr
	A* b = new C();
	int* q = (int*)b;
	p[0] = q[0];
	a->Print1();
	a->Print2();

	system("pause");
	return 0;
}

 

 

__vfptr更改之前

__vfptr更改之后

可以看到通过多态类对象指针首个元素就是__vfptr虚函数表指针,我们可以在运行时显示通过虚函数指针访问虚函数和以及改变虚函数表的地址,或者改变虚函数表存储的地址

 

RTTI

RTTI(run-time type identification), 运行时类型标识. 可以在Runtime拿到各种数据的一些简单类型信息, 如名字等等.

RTTI在c++的两个应用为 typeid dynamic_cast

typeid

int main()
{
	A* a = new B();
	A* aa = new A();
	A* c = new B();
	C* d = new C();
	int e = 1;
	cout << typeid(a).name() << endl;
	cout << typeid(aa).name() << endl;
	cout << typeid(d).name() << endl;
	cout << typeid(e).name() << endl;
	system("pause");
	return 0;
}

可以看出typeid类型是由目前的赋值对象类型决定,而非创建的对象。

dynamic_cast

class A
{
public:
	A(){}

	void Test(){}

	virtual void Print1(){}

	virtual void Print2(){}
};

class B : public A
{

public:
	B(){}

	virtual void Print2() override{}
};

class C
{
public:
	C() {}

	void Print1() {}
};

class D : public C
{
public:
	D(){}

	void Print1() {}
};

int main()
{
	A* a = new B();
	B* b = dynamic_cast<B*>(a);
	if (b != nullptr)
	{
		printf("b is not null");
	}

	C* c = new D();
	D* d = dynamic_cast<D*>(c);
	if (d != nullptr)
	{
		printf("d is not null");
	}

	system("pause");
	return 0;
}

可以看到在编译期没有虚函数表(多态类型)的不能进行dynamic_cast

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值