本文通过简单例子说明子类之间发生强制转换时虚函数如何调用,旨在对c++继承中的虚函数表的作用机制有更深入的理解。
#include<iostream> using namespace std; class Base { public: virtual void f() { cout<<"Base::f()"<<endl; } }; class child1:public Base { public: virtual void f() { cout<<"child1::f()"<<endl; } virtual void a() { cout<<"child1::a()"<<endl; } }; class child2:public Base { public: virtual void f() { cout<<"child2::f()"<<endl; } virtual void b() { cout<<"child2::b()"<<endl; } virtual void a() { cout<<"child2::a()"<<endl; } }; int main() { child1 c1; child2* pc21=(child2*)&c1; pc21->b();//输出 child1::a()// pc21->a();//访问越界,程序运行时崩溃 child2 c2; child1* pc12=(child1*)&c2; pc12->a();//输出 child2::b() return 0; }
结论:
1、通常的类型强转是告诉编译器必须按照指定结构的内存布局来解析对应内存,如上例中“child2* pc21=(child2*)&c1; ”,编译器会把c1对应的内存来当做Derive的内存布局来解析。因为在对象c1对应的类child1的虚函数表中,共存在三个函数,分别为f() b() a(),其中函数b()是第二个,因此编译器就会把对象c1对应的内存来当做类child2的内存布局来解析(注意内存里的内容不变,还是c1的,即为类child1的内存布局,在这里只有虚函数表),此时在类child1的虚函数表中也找第二个函数,找到了函数a(),因此输出“child1::a()”,运行正常。但这种行为可能是危险的,若使用的内存布局并不适合真实内存,很可能造成访问越界等问题(如上例中的“pc21->a();”,这次就在类B的虚函数表中找第三个函数,结果没有找到(访问越界),函数运行时崩溃。),因此使用强制转换操作时应特别注意。
2、通过上述例子可知,虚函数在虚函数表中的存储顺序是与声明顺序一致的,而不是虚函数名字的字符串排序,如本例中为f() b() a(),虽然编程时的自动补全提示框中显示的顺序是a() b() f(),但可能已经经过内部优化,这个就不太清楚了(也不是我们要研究的内容)。
时间: 2024-10-25 17:22:48