发布时间:2023-03-29 17:00
作者:小 琛
欢迎转载,请标明出处
先来看一道题目:
求代码中输出值为多少?
#include
class text
{
public:
virtual void Func1()
{
std::cout << \"Func1()\" << std::endl;
}
private:
int _b = 1;
};
int main()
{
text temp;
std::cout << sizeof(temp) << std::endl;
return 0;
}
很多人在这里都会犯错,回答成4,其实结果如下:
这是为什么呢?
我们通过调试,借助变量窗口来分析
可以看到,在temp对象中,存在两个成员,一个是本事定义的变量int _b,而另一个变量_vfptr,是一个\"数组指针”,我们并没有定义它,而它却存在于对象中!
重要结论!!!
也就是说,虚函数在类中的存储是通过一个指针,该指针指向一个数组,对应的虚函数指针分别存放于该数组中。我们将这个指针称之为虚表指针
我们大胆猜想,是否和虚函数的定义有关呢?
接着,我们写一份完整的多态代码。
#include
using namespace std;
class Base
{
public:
virtual void Func1()
{
cout << \"Base::Func1()\" << endl;
}
virtual void Func2()
{
cout << \"Base::Func2()\" << endl;
}
void Func3()
{
cout << \"Base::Func3()\" << endl;
}
private:
int _b = 1;
};
class Derive : public Base
{
public:
virtual void Func1()
{
cout << \"Derive::Func1()\" << endl;
}
private:
int _d = 2;
};
void function(Base& v)
{
v.Func1();
}
int main()
{
Base b;
Derive d;
function(b);
function(d);
return 0;
}
再次借助调试窗口来分析:
看图中的那么数组指针,该指针指向的数组的内容在监视窗口也可以看到,注意看下图中红色圈起部分
我们发现它们内容是不同的,父类型b中存放的是基类Base的两个成员函数的函数指针,分别为Func1于Func2,但派生类d中存储的是从父类继承来的Func2与derive本身重写的的Func1
总结以下几点
我们再次回顾多态的构成条件:
这样就不难理解其中的道理了,因为要完成虚表中的覆盖,则派生类就必须对虚函数进行重写
笔者来对以上内容进行总结:
虚函数在类中的存放,就是在这样的类中,会生成一个指针,该指针是一个数组指针,我们通常将这个数组成为虚表,虚表内存放的则是这些虚函数的函数指针。
而在实现多态的时候,派生类继承父类,同样会有一个数组指针,这个指针所指向的数组(虚表)存放的内容分为两部分:一是自己本身的虚函数,二是从父类继承来的。但从父类继承来的部分虚函数会因为实现多态而被我们重写,因此进行覆盖掉。
所产生的结果就是父类的虚表和子类的虚表内容并不相同。 而我们通过指针或者引用进行不容对象的调用的时候,不同的对象会在虚表内找到相应的函数指针从而执行,和上面例子相匹配的就是b会调用父类的Func1(),而d会调用派生类的Func1(),以此达到多态!