虚函数运行机制

class A {
public:
    virtual void foo (void) { ... }
    virtual void bar (void) { ... }
};
class B : public A {
public:
    void foo (void) { ... }
};
int main(){
    A* pa = new A;
    pa->foo (); // A::foo
    pa->bar (); // A::bar
---------------------
    A* pa = new B;
    pa->foo (); // B::foo
    pa->bar (); // A::bar
}

如上边例子所示,如果A中有了关键字virtual,pa调用哪个函数就不是根据自己指针类型而定的,相反起决定作用的是指针目标,多态的实质就是虚函数+指针(引用)。用下图对上边结果进行描述:

编译器会为每个有虚函数的类创建一个虚函数表,该虚函数表将被该类的所有对象共享。类的每个虚成员占据虚函数表中的一行。如果类中有N个虚函数,那么其虚函数表将有N*4字节的大小。虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其真实反应实际的函数。

虚函数表是一张函数查找表,用以解决以动态联编方式调用函数。它为每个可以被类对象调用的虚函数提供一个入口,这样当我们用基类的指针或者引用来操作子类的对象时,这张虚函数表就提供了编译器实际调用的函数。虚函数表其实是存储了为类对象进行声明的虚函数地址。当我们创建一个类对象时,编译器会自动的生成一个指针*__vptr(一个隐藏指针),该指针指向这个类中所有虚函数的地址表。(实际上,虚函数表就是一个函数地址数组表。),请注意,*__vptr和*this指针不同,*this是一个被编译器用作解决自引用的函数参数,而*__vptr则是一个真正的指针。每一个类,不管是基类还是子类都有一个自己的virtual
table,*__vptr也是被继承过来的。

下边我们来举个例子对此进行验证:

#include <iostream>
using namespace std;
class A {
public:
	virtual void foo (void) {
		cout << "A::foo()" << endl;
	}
	virtual void bar (void) {
		cout << "A::bar()" << endl;
	}
};
class B : public A {
public:
	void foo (void) {
		cout << "B::foo()" << endl;
	}
};
int main (void) {
	A a;
	cout<< "a的地址:"<<&a<<endl;
	void (**vft) (void) = *(void (***) (void))&a;
	cout << (void*)vft[0] << ' '
		<< (void*)vft[1] << endl;  //如果没转换为void* 显示两个bool类型的1 ,void*对<< 实现了重载,可以显示正确的地址
	vft[0] ();  //调的是A::foo
	vft[1] ();// A::bar
	cout << endl << endl;

	B b;
	cout<< "b的地址:"<<&b<<endl;
	vft = *(void (***) (void))&b;
	cout << (void*)vft[0] << ' '
		<< (void*)vft[1] << endl;
	vft[0] (); // B::foo
	vft[1] ();  //A::bar
	return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-29 15:49:45

虚函数运行机制的相关文章

C++虚函数运行机制

C++ 虚函数表解析 前言 C++中的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数.这种技术可以让父类的指针有"多种形态",这是一种泛型技术.所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法.比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议. 关于虚函数的使用方法,我在这里不做过多的阐述.大家可以看看相关的C++的书籍.在这篇文章中,我只想从虚函

虚函数实现机制

说到虚函数的实现方法,我们就不得不说到动态联编(dynamic binding)和静态联编(static binding).静态联编意味着编译器能够直接将标识符和存储的物理地址联系在一起.每一个函数都有一个唯一的物理地址,当编译器遇到一个函数调用时,它将用一个机械语言说明来替代函数调用,用来告诉CPU跳至这个函数的地址,然后对此函数进行操作.这个过程是在编译过程中完成的(注:调用的函数在编译时必须能够确定),所以静态联编也叫前期联编(early binding).但是,如果使用哪个函数不能在编译

C++虚函数实现机制

C++中虚函数的实现机制主要是VTable和虚指针.详细如下: class A { public: virtual void f1(); virtual void f2(); private: int a; } class B { public: void f1(); private: int b; } 如上A,B两个类,编译器为A类准备了一个虚表VTableA如下: A::f1的地址 A::f2的地址 编译器为B类准备的VTableB如下:    B::f1的地址    A::f2的地址 B类

一个证明signal函数运行机制的函数!

1 #include<stdio.h> 2 #include<signal.h> 3 void SignHandler(int iSignNum) 4 { 5 printf("\nCapture signal number is:%d\n",iSignNum); 6 } 7 int main(void) 8 { 9 int i; 10 signal(SIGINT,SignHandler); 11 while(1) 12 { 13 for(i=0;i<10;

探究PHP的函数运行机制

在任何语言中,函数都是最基本的组成单元.对于php的函数,它具有哪些特点?函数调用是怎么实现的?php函数的性能如何,有什么使用建议?本文将从原理出发进行分析结合实际的性能测试尝试对这些问题进行回答,在了解实现的同时更好的编写php程序.同时也会对一些常见的php函数进行介绍. PHP函数的分类 在php中,横向划分的话,函数分为两大类:user function(内置函数)和internal function(内置函数).前者就是用户在程序中自定义的一些函数和方法,后者则是php本身提供的各类

匹夫细说C#:从园友留言到动手实现C#虚函数机制

前言 上一篇文章匹夫通过CIL代码简析了一下C#函数调用的话题.虽然点击进来的童鞋并不如匹夫预料的那么多,但也还是有一些挺有质量的来自园友的回复.这不,就有一个园友提出了这样一个代码,这段代码如果被编译成CIL代码的话,对虚函数的调用会使用call而非callvirt: override string ToString() { return Base.ToString(); } 至于为何是这样,匹夫在回复中也做了解释,因为上面那段代码其实相当于是这样的: override string ToSt

C++ 虚函数和虚继承浅析

本文针对C++里的虚函数,虚继承表现和原理进行一些简单分析,有希望对大家学习C++有所帮助.下面都是以VC2008编译器对这两种机制内部实现为例. 虚函数 以下是百度百科对于虚函数的解释: 定义:在某基类中声明为 virtual 并在一个或多个派生类中被重新定 义的成员函数[1] 语法:virtual 函数返回类型 函数名(参数表) { 函数体 } 用途:实现多态性,通过指向派生类的基类指针,访问派生类中同名覆盖成员函数 函数声明和定义和普通的类成员函数一样,只是在返回值之前加入了关键字"vir

C++ 虚函数 、纯虚函数、接口的实用方法和意义

也许之前我很少写代码,更很少写面向对象的代码,即使有写多半也很容易写回到面向过程的老路上去.在写面向过程的代码的时候,根本不管什么函数重载和覆盖,想到要什么功能就变得法子的换个函数名字,心里想想:反正函数重载本质也就是入栈了两个不同的函数. 回过头来讲,让我了解标题这三个概念的实际用处,还是在于我这第四次重写毕业论文的代码,将它改写成面向对象的时候,才理解的.在面向对象设计的过程中, 类是从抽象逐渐具体起来的,父类可以是非常非常抽象的东西,而最终实例化的子类就非常具体了.在这个继承的过程中,不断

虚函数和模板编程的一点共性和特征模板的一个例子

最近在看元编程中,对虚函数和模板编程有一点点感悟,写一篇博客简单总结一下. 虚函数和模板是C++里面很棒的特征,他们都提供了一种方法,让程序在编译中完成一些计算,去掉的这些计算在比较low的编程方式中,是需要在程序运行中执行的.在这里,我要强调的是:"在编译过程中完成一些计算". 我会举两个例子,一个是虚函数的,比较简单,另一个例子是关于特征模板的,在例子中,根据模板参数的类型自动选择模板的底层数据结构. 第一个例子是比较简单的虚函数的例子,有很多种水果的类型,我们有一个函数要展示他们