虽然这个图是疯狂的寻找,这真的很简单:* __vptr每班点的虚拟表类。在最派生版本这类可以调用函数的对象的虚拟点表条目。
所以考虑会发生什么,我们创建的一个对象时,D1型:
1
2
3
4
|
int
main()
{
D1 cClass;
}
|
因为阶层是D1对象,阶层有* __vptr设置为D1虚拟表。
现在,让我们把基类指针D1:
1
2
3
4
5
|
int
main()
{
D1 cClass;
Ba
|
注意,因为pclass是基类指针,它指向阶层的基部。然而,也要注意* __vptr是在类的基础部分,所以pclass访问这个指针。最后,请注意,pclass -> __vptr指向D1虚拟表!因此,即使pclass类型为基础,它还能访问D1′虚拟表。
所以当我们试着打pclass -> function1()?
1
2
3
4
5
6
|
int
main()
{
D1 cClass;
Base *pClass = &cClass;
pClass->function1();
}
|
首先,该计划承认,function1()是一个虚拟函数。第二,采用pclass -> __vptr到D1′虚拟表。第三,它看起来打电话在D1′虚拟表function1()版本。这已被设置为D1::function1()。因此,pclass -> function1()解决D1::function1()!
现在,你可能会说,“但如果基地确实指向代替D1对象基类对象。它还叫D1::function1()?“。答案是否定的
1
2
3
4
5
6
|
int
main()
{
Base cClass;
Base *pClass = &cClass;
pClass->function1(
|
在这种情况下,当阶层被创建,__vptr分基地的虚表,不是D1′虚拟表。因此,pclass -> __vptr也将指向基地的虚拟表。为function1()分基地的虚拟表中的条目::function1()。因此,pclass -> function1()解决基地::function1(),这是最派生版本function1(),基类对象应该能打电话。
通过使用这些表,编译器和程序可以确保函数调用解决适当的虚拟函数,即使你只使用基类的指针或引用!
调用虚函数调用非虚函数是有几个原因:第一,我们要慢,使用* __vptr到适当的虚拟表。 第二,我们要索引的虚拟表,找到正确的函数调用。 只有这样我们才能调用该函数。 因此,我们要做的3个操作找到的函数调用,而不是2操作正常的间接函数调用,或一个操作的一个直接的函数调用。 然而 , 随着 现代计算机, 这 增加的时间 通常是相当 微不足道 。