虚函数是C++中一个重要的概念,搞清楚这个概念对于理解C++运行的内在机制有一定的帮助。下面通过一个例子来总结一下C++中虚函数的调用原理。
示例代码:
#include <iostream>
#include <cstdlib>
using namespace std;
class A
{
public:
void set(int i,int j)
{
x=i;
y=j;
}
virtual int get()
{
return x+y;
}
private:
int x;
int y;
};
class B:public A
{
public:
void set(int i,int j)
{
x=i;
y=j;
}
int get()
{
return x+y;
}
private:
int x;
int y;
};
int main(int argc,char *args[])
{
B b;
b.set(4,6);
B &rb=b;
cout<<rb.get()<<endl;
A a;
a.set(7,8);
cout<<a.get()<<endl;
A &ra=b;
cout<<ra.get()<<endl;
system("pause");
return EXIT_SUCCESS;
}
对于上述代码,下面是通过基类的引用来调用虚函数时(ra.get())所经历的几个阶段:
1. 开始调用ra.get(),在运行中引用ra可以判断到底指向了哪个对象(基类or派生类)
2. 取得对象的vtable的指针
3. 从vtable那里获得函数入口的偏移量,即得到要调用的函数的指针
4. 根据vtable的地址找到函数,并调用函数
一个函数说明为虚函数,表明在继承的类中重载这个函数时,到调用这个函数时应该确定调用哪个对象的这个函数。这也就是所谓多态的机制,多态使得我们可以通过基类的引用或指针来指明一个对象(包括其派生类的对象),当调用函数时可以自动判断调用的是哪个对象的函数。
C++编译器对普通函数和虚函数有着两种非常不同的处理方式。
对于
普通函数处理而言,特定的函数都会映射到特定的代码,无论是编译阶段还是链接阶段, 编译器都可以计算出要处理函数的地址,直接调用即可。
而对于
虚函数而言,被调用的函数不仅仅需要依据调用的特定函数,还需要依据调用对象的种类进行判断。通常情况下都是通过一个叫虚函数表(vtable)来实现的。
虚函数表的结构
虚函数表是一个函数指针表,每个表项都指向一个函数。任何一个包含至少一个虚函数的类都会有这样一张表。需要注意的是vtable只包含虚函数的指针,没有函数体。实际上就是一个函数指针的数组。虚函数表既有继承性又有多态性。每个派生类的vtable继承了它各个基类的vtable,如果基类vtable中包含某一项,则它的派生类的vtable中也将包含同样的一项,但是这两项的值可能是不同的。如果派生类重载(override)了该项对应的虚函数,则派生类vtable的该项指向重载后的虚函数。若没有重载的话,则直接沿用基类的值。
特别注意的是,每一个类中仅有唯一的一个vtable,不是每个对象都有一个vtable。但是每个对象都有一个指向所属类的vtable的指针(此类包含虚函数的前提下)。每个对象额外增加了一个指针的大小,通常是4个字节。
此外,还需要注意的是基类和派生类使用的vtable是物理上独立的,即它们具有不同的地址。它们唯一的联系就是,当派生类没有实现基类虚函数的重载时,派生类会将自己vtable中对应函数的地址设定为基类函数的地址。
Reference
1. 《从新手到高手C++全方位学习》 PP.245 系统是如何调用虚函数的
2. http://www.cppblog.com/ElliottZC/archive/2007/07/20/28416.aspx
分享到:
相关推荐
当一个基类指针或引用指向派生类的对象时,通过调用虚函数,可以执行派生类中的版本,而不是基类的版本。这种机制使得代码更加灵活,可以处理不同类型的对象,而无需知道其具体类型。 然而,这里有一个陷阱:如果...
本文将深入解析C++中类中函数调用的基本原理、语法结构及其实现方式,通过一个具体的代码示例来展示这一过程。 ### 类中函数调用的基础概念 在C++中,类是一种用户定义的数据类型,它不仅可以包含数据成员(即属性...
总结来说,这个实例展示了C++中如何通过对话框和逻辑类实现类间函数调用,以及如何在用户交互后执行特定的逻辑操作。这种设计模式在实际开发中非常常见,可以提高代码的可读性和可维护性。通过理解这个实例,开发者...
虚函数调用的过程如下: 1. 当通过基类指针调用虚函数时,编译器会在生成的机器代码中插入一条指令,获取对象的虚指针(VPTR)。 2. 接着,编译器使用VPTR来查找虚函数表(VTABLE)中对应的函数地址。 3. 最后,执行...
C++ 虚函数是 C++ 编程语言中一个重要的概念,它允许在编译期和运行期进行函数调用和绑定。虚函数的工作原理是通过虚函数表来实现的,该表记录了虚函数的地址,解决继承、覆盖的问题,保证动态绑定时能够根据对象的...
在上面的代码中,虽然没有直接展示虚函数表,但其工作原理如下:`base_class`、`drived_class1`和`drived_class2`每个类都有自己的vtable,`pbc`指向不同对象时,调用虚函数实际上是在访问相应的vtable并执行相应的...
### C++虚函数表解析深度剖析 在C++编程语言中,虚函数是实现多态性的关键机制之一,尤其在...通过本文的深入解析,希望能帮助读者更加深刻地理解C++虚函数表的工作原理,为进一步探索和应用多态性打下坚实的基础。
虚函数是C++中面向对象编程的一个重要特性,它允许我们通过基类指针或引用调用派生类中的重写方法,实现多态性。本文将深入探讨如何使用C++实现虚函数,包括虚函数的基本用法、虚析构函数的概念以及如何计算类的大小...
总结来说,C++的虚函数机制是通过虚函数表来实现多态性,使得我们能够在运行时根据对象的实际类型动态地调用合适的函数。理解这一机制对于深入学习C++的继承和多态性至关重要,同时也有助于掌握其他依赖于此机制的...
C++虚函数表详解 C++中的虚函数表是实现多态机制的关键组件。虚函数表(Virtual Table,简称V-Table)是一种机制,用于存储类的虚函数的地址,解决继承和覆盖的问题,使得父类的指针可以正确地调用子类的成员函数。...
虚函数是C++中实现多态性的一种关键机制,它允许我们通过基类指针或引用调用派生类中的重写版本。这种机制被称为动态绑定或运行时多态,与静态绑定(编译时多态)相对。下面我们将深入探讨虚函数的原理及其在C++中的...
例如,如果有基类`Base`和派生类`Derived`,`Base`有一个虚函数`void func()`, 那么即使通过`Base`类型的指针或引用调用`func()`,如果实际指向的对象是`Derived`类型,那么`Derived`中重写的`func()`将会被调用,这...
在C++中,虚函数是一个在基类中声明并可以被派生类重写的成员函数。通过使用`virtual`关键字声明,它允许我们使用基类指针或引用调用派生类的版本,而不仅仅是基类的版本。这样就实现了动态绑定(也称为晚期绑定),...
用c++类的实例解析虚函数表,查看虚表的地址,利用虚函数表中的地址调用虚函数实现C++中的多态特性, 此例是解析虚函数中简单易于理解的示例,重要的地方具有详细的注释。亲测可用,若有不清除的地方,可以留言,...
假设我们有一个基类`ClassA`和一个继承自`ClassA`的派生类`ClassB`,并且`ClassA`中定义了一些虚函数,`ClassB`重写了这些虚函数。当我们使用以下两种方式创建对象时: 1. `ClassA *a = new ClassB();` 2. `ClassB ...
在C++编程语言中,虚函数表(Virtual Function Table,简称vtable)是实现多态性的一个关键机制。...通过阅读“C++虚函数表解析.docx”文件,你可以更深入地了解这个主题,包括其实际应用和潜在的优化策略。
C++虚函数是面向对象编程中的一个重要特性,它允许我们实现多态性,即一个基类指针或引用可以调用派生类重写的成员函数。这种能力在编写可扩展和可复用的代码时非常关键。下面我们将深入探讨C++虚函数的应用及其实现...