C++深度探索虚函数指针示例
Huuaaaaa 人气:0代码描述:定义一个Person类为基类,ChinesePer 类与EnglishPer类都继承于此基类。
class Person { public: void speak() { cout << "说人话" << endl; } private: int m_type = 1 ; }; class ChinesePer :public Person { public: void speak() { cout << "说中国话chinese..." << endl; } }; class EnglishPer :public Person { public: void speak() { cout << "说英国话english..." << endl; } };
先看看此时各个类占用的内存信息:
int main() { int person_size = sizeof(Person); int Chineseper_size = sizeof(ChinesePer); int Englishper = sizeof(EnglishPer); }
可以看到三个类的大小都为4个字节,占用的情况就是 类中的m_type变量。
将基类中的 speak() 函数加上virtual关键字 ,成为虚函数后再次查看内存字节大小:
可以看到多出了4个字节大小的空间(X86),三个类的大小都为 8。其实就是多出了一个指针的大小,4个字节 ,创建一个子类对象就可以明显看出来:
可以得出结论一:继承过来的成员变量 m_type(4个字节) + _vfptr(4个字节) = 8个字节。
继续探索虚函数表的原理, 创建以下子类对象并通过父类指针指向子类对象调用speak函数发生多态:
int main(){ ChinesePer chs; ChinesePer chs2; EnglishPer eng; Person *ptr = &chs; //父类指针指向子类对象 Person *ptr1 = &chs2; Person *ptr2 = ŋ ptr->speak(); ptr1->speak(); ptr2->speak(); }
以上代码执行后会发生多态:
此时我们看看三个对象的内存分布:
用图片简要描述一下就是:
在这里可以先得到结论二: 同一个子类的所有对象共享一个虚函数表,指向虚函数表的指针_vptr是相同的。
在内存分布上查看一下 eng对象的虚函数指针地址情况:
可以看到该虚函数指针的地址上的值,存放的正是 该子类中的重写函数 speak()函数的地址。
如果把这个地址的值,修改为 Chineseper 类中的重写函数speak() 的地址值,会发生什么?
手动修改了eng对象中虚函数指针内的地址值,将改值本来是存放的是 EnglishPer 类中的speak() 函数的地址,现在更改为 ChinesePer 类中speak() 函数的地址。
单步走发生多态:
至此可以得出结论三:
当基类函数加了virtual 关键字后,虚函数的调用方法是间接调用:先查虚函数表的地址(也就是指向虚函数表的指针 _vptr),再查虚函数表中的虚函数指针。
不妨在基类继续添加两个虚函数
class Person { public: virtual void speak() { cout << "说人话" << endl; } virtual void eat() { cout << "吃饭" << endl; } virtual void sleep() { cout << "睡觉" << endl; } private: int m_type =1 ; };
子类只重写了speak函数,查看一下chs对象的虚函数指针地址存放的值:
的确存放的还是各个函数的地址,且是连续存放的,因此在进行查表调用虚函数的时候,也是每移动4个字节指向的就是一个函数的指针地址。
可以简要描述一下形式就很直观了:
总结:
1.增加了virtual 关键字的对象头部4个字节是一个指针,指向了虚函数表的地址(单继承情况下)。
2.同一个子类的所有对象共享一个虚函数表,指向虚函数表的指针_vptr是相同的。
3.当基类函数加了virtual 关键字后,虚函数的调用方法是间接调用:先查虚函数表的地址(也就是指向虚函数表的指针 _vptr),再查虚函数表中的虚函数指针。
加载全部内容