一、虚函数和普通函数的区别
classAnimal
{
public:
virtualvoidFunc1();
};
classFish:publicAnimal
{
public:
virtualvoidFunc1();
voidFunc2();
};
intmain(intargc,char*argv[])
{
Animal*an;
Fish*fi;
return0;
}
-->第一类:
最简单的an可以调用Func1();fi可以调用Func1();Fun1();(用的都是自己的)
-->第二类:
如果设置an=fi;结论是:an可以调用Func1();fi可以调用Func1();Func2();
如上两种情况所说其实给进行an=fi;其实没啥大用,因为他们都调用了自己的函数;
但是有一种情况我们是需要解决的我们希望使用父类的指针来模拟子类的行为
或者说我们希望使用一根父类的指针来调用我们在子类里"覆盖"了的函数
那么我们需要使用"虚函数";
这样我们就可以使用一根父类的an指针然后来模拟在子类中被"override"的函数Func1();
这就是多态,在未引入"虚函数"之前父类总会根据内存模型来调用父类的函数;
引入了"虚函数"之后父类就会(在运行时采用"迟绑定")去检查是否有子类override了父类的virtual函数;
然后确定调用的是哪一个函数(子类的?父类的?"子类优先");
"多态的实现":
当编译器发现一个类中有虚函数时;就会为这个类自动生成一个"虚表";该表是一个一维数组,在这个数组中存放每个虚函数的地址
而且会生成一个"指向虚表指针";那么对于父类Animal有一个"虚表",子类Fish由于一定有虚函数所以也有一个"虚表"
现在在来结合上面的信息;"编译器按照an的内存结构来解释fi";这里就发生了"两个动作":(an小fi大才会截断大给小ok小给大GG)
第一个是"内存的截断":这就解释了为什么fi自己的函数这里用不了了;因为内存截断了,(对于an,fi自己的子类部分成垃圾了)
第二个是"虚表指针改变":一开始an的虚表指针应该指向着自己的虚函数,fi的虚表指针也指向了自己的虚函数;
而此时an指向了fi子类对象当按照an的内存结构解释fi时解释到的是fi的"虚表";所以调用到了fi的虚函数;
类里面的东西非常复杂.......不是那么简单的判断而已
二、虚函数的字节长度
虚函数会在对象占用内存开始处增加4个字节(无论虚函数有多少个)。这4个字节是指向“虚函数表”的指针,即指向一个函数地址表,函数地址再指向对应的函数实现。
在用sizeof时有一点需要注意,即内存的对齐。
字符型占用15个字节,内存对齐,内存中占用16字节,虚函数占用4字节,
所以共占用20字节
关于虚函数详解到此分享完毕,希望能帮助到您。