https://blog.csdn.net/u010261063/article/details/70064090
1 #include <iostream> 2 #include <memory.h> 3 using namespace std; 4 5 class parent{ 6 public: 7 virtual void output(); 8 virtual void output2(); 9 }; 10 11 void parent::output(){ 12 cout << "parent output" << endl; 13 } 14 15 void parent::output2(){ 16 cout << "parent output2" << endl; 17 } 18 19 class son : public parent{ 20 public: 21 void output(); 22 }; 23 24 void son::output(){ 25 cout << "son" << endl; 26 } 27 28 int main(){ 29 son s; 30 // memset(&s, 0, sizeof(s)); // 会段错误 31 cout << "sizeof(s)=" << sizeof(s) << endl; // 输出为8 没虚函数时1 32 parent& p = s; 33 p.output(); 34 return 0; 35 }
输出程序运行结果:程序不输出结果,运行出错!!分析一下原因:
在使用memset初始化对象Obj之前,通过Obj调用output函数时程序运行正常,但是一旦利用Memset函数初始化该对象,再对该obj调用Show和Print函数,则程序立马崩溃。
究其原因是因为初始化obj的时候,将obj包含的指向虚函数表VTBL的指针也清除了。包含虚函数的类对象都有一个指向虚函数表的指针,此指针被用于解决运行时和动态类型强制转换时虚函数的调用问题。
该指针是被隐藏的,对程序员来说,这个指针也是不可存取的。当进行memset操作时,这个指针(即指向虚函数表的地址)的值也要被初始化,这样一来,只要一调用虚函数,程序便会崩溃。
这种现象在很多由C转向C++的程序员来说,很容易犯这个错误,而且这个错误很难查。
为了避免这种情况,记住对于有虚拟函数的类对象,决不能使用memset来进行初始化操作。而是要用缺省的构造函数或其它的init例程来初始化成员变量。
当类中有虚函数的时候,编译器会为类插入一个我们看不见的数据并建立一个表。这个表就是虚函数表(vtbl),那个我们看不见的数据就是指向虚函数表的指针——虚表指针(vptr)。
虚函数表就是为了保存类中的虚函数的地址。我们可以把虚函数表理解成一个数组,数组中的每个元素存放的就是类中虚函数的地址。
当调用虚函数的时候,程序不是像普通函数那样直接跳到函数的代码处,而是先取出vptr即得到虚函数表的地址,根据这个来到虚函数表里,从这个表里取出该函数的地址,最后调用该函数。
所以只要不同类的vptr不同,他对应的vtbl就不同,不同的vtbl装着对应类的虚函数地址,这样虚函数就可以完成它的任务了。