虚函数是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++中的多态特性, 此例是解析虚函数中简单易于理解的示例,重要的地方具有详细的注释。亲测可用,若有不清除的地方,可以留言,...
介绍了C++编程语言中的虚函数及其在进行面向对象程序设计中重要性,并且详细阐述了它在...它通过一个vptr和vtable在运行时进行动态绑定,从而能够根据对象类型的不同调用不同的 虚函数;并通过实例测试验证了上述机制.
C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际 子类的成员函数。
1. 从存储空间角度,虚函数对应一个指向vtable虚函数表的指针,这大家都知道,可是这个指向vtable的指针其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有...
C++_虚函数表解析 C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这...
这样,在有虚函数的类的实例中这个表被分配在了 这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。
C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。
虚函数是在基类定义,然后子类重写这个函数后,基类的指针指向子类的对象,可以调用这个函数,这个函数同时保留这子类重写的功能。 纯虚函数是可以不用在基类定义,只需要声明就可以了,然后因为是纯虚函数,是不能...
这样,在有虚函数的类的实例中这个表被分配在了 这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。
这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,VFTable就显得尤为重要了,它就像一个地图一样,指明了实际所应该调用的函数。在C++的标准中提到,...
《Visual C++2012入门经典(第6版)》实例,使用指向基类对象的指针调用虚函数,使用引用处理虚函数
C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。...
多态是C++面向对象程序设计的一个重要特性。以前看到虚函数觉得很神奇,为...当我们通过父类指针来操作一个子类的时候,调用虚函数,都是通过虚函数表+固定的偏移,这样运行期多态便实现了。 在深度《深度》这本书中
多态是c++的特点之一,关于多态,简而言之就是 用父类的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数,这种方法呢,可以让父类的指针具有多种形态,也就是说不需要改动很多的代码就可以让父类这...
对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)和一个指向虚函数表的指针(vptr)来实现的。虚函数表,简称为vtbl,虚函数表表对实现多态起着至关重要
C++ 中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技 术可以让父类的指针有“多种形态”,这是一种泛型技术...