1.非virtual函数,调用规则取决于对象的显式类型。例如

A* a  = new B();

a->display();

调用的就是A类中定义的display()。和对象本体是B无关系。

2.virtual函数,具体调用哪个版本,取决于虚函数表。例如

A* a = new B();

a->v_display();

这个时候,对象a就需要查找自身的虚函数表,表中的v_display()是一个函数指针,可能指向不同类中的对应的v_display函数并调用对应版本的v_display。一般而言,如果B重写了v_display(),那么会调用B的v_display(),如果没有会调用A的display();

3.纯虚函数。结合2分析,纯虚函数在父类是没有定义的,如果子类继承后仍然不重写并定义对应的纯虚函数,那么子类也是抽象类,无法初始化对象,直到有一个类定义了这个虚函数。

这个类才可以初始化对象。并在这个对象上维护这个函数的虚函数表相关数据。

关于虚函数表:

一般存储在一个对象的固定的地址,我们甚至可以通过虚函数表来直接调用某些虚函数。

例如:

A* a = new B();

a就是对象a的地址

将a的地址强制转化成int型指针 (int*)a ,也就是理想化将对象看成int型数组。

而上述这些地址中每个地址解引用会获得什么呢?别的不知道,第一个存储的就是虚函数表,里边又是一组地址!我们将第一个地址解引用:

*(int*)(a)

然后得到了一个存储虚函数表指针的首地址(如果直接打印会打印的int十进制),然后再将其强转成int*,把这个十进制的数值看成一个int*型指针,这样虚函数表的第一个地址我们也得到了,那就是

(int*)*(int*)(a)

这个地址可以像int[] 一样计算 ,如果+1 就可以获得虚函数表的下一个函数。但是毕竟得到的都是每个函数的地址,所以我们还需要将它解引用:

*(int*)*(int*)(a) 这样就获得真实的函数指针了!

到这里就简单了,假如我们调用的函数是void(void)类型的,我们创建一个函数指针类型:

typedef void(*Fun)(void)

这样如果上边从虚函数表中获得的函数指针也是void(void)类型的,我们就可以将其强转为对应的函数指针类型

Fun vfun = (Fun)*(int*)*(int*)(a);

vfun();

哦卖萧的。。。。。。

我这里有一组测试代码:

#include <iostream>
#include <string>

using namespace std;

typedef void(*Fun)(void);

class A
{
private :
    string s;
public :
    virtual void display();
    virtual void v_display();
    ;
};

class B : public A
{
public:
    void display();
    //void v_display();
    void v_0_display () const;
};

void A::display()
{
    cout << "A::display" << endl;
}

void A::v_display()
{
    cout << "A::v_display" << endl;
}

void B::display()
{
    cout << "B::display" << endl;
}

//void B::v_display()
//{
//    cout << "B::v_display" << endl;
//}

void B::v_0_display() const
{
    cout << "B::v_0_display" << endl;
}

int main(int argc, char* argv[])
{
    A* a = new B();
    B* b = new B();

    Fun vfun = NULL;
    vfun = (Fun)*(();
    cout << ) << endl;
    cout << "------------virtual table-------------" << endl;
    vfun();
    vfun = (Fun)*(();
    vfun();
    vfun = (Fun)*(();
    vfun();
    cout << "--------------------------------------" << endl;

    a->display();
    a->v_display();
    a->v_0_display();
    ((B*)(a))->display();
    ((B*)(a))->v_display();
    ((B*)(a))->v_0_display();
    b->display();
    b->v_display();
    b->v_0_display();
    ((A*)(b))->display();
    ((A*)(b))->v_display();
    ((A*)(b))->v_0_display();

    system("pause");
}

打印结果为:

the address the first virtual function is002FDC94
------------virtual table-------------
B::display
A::v_display
B::v_0_display
--------------------------------------
B::display
A::v_display
B::v_0_display
B::display
A::v_display
B::v_0_display
B::display
A::v_display
B::v_0_display
B::display
A::v_display
B::v_0_display
请按任意键继续. . .

C++ 多态、虚函数机制以及虚函数表的更多相关文章

  1. C++中对C的扩展学习新增内容———面向对象(继承)函数扩展性及虚函数机制

    1.c语言中的多态,动态绑定和静态绑定 void do_speak(void(*speak)()) { speak(); } void pig_speak() { cout << &quo ...

  2. 浅谈C++虚函数机制

    0.前言 在后端面试中语言特性的掌握直接决定面试成败,C++语言一直在增加很多新特性来提高使用者的便利性,但是每种特性都有复杂的背后实现,充分理解实现原理和设计原因,才能更好地掌握这种新特性. 只要出 ...

  3. [C/C++] 虚函数机制

    转自:c++ 虚函数的实现机制:笔记 1.c++实现多态的方法 其实很多人都知道,虚函数在c++中的实现机制就是用虚表和虚指针,但是具体是怎样的呢?从more effecive c++其中一篇文章里面 ...

  4. C++ 类的多态三(多态的原理--虚函数指针--子类虚函数指针初始化)

    //多态的原理--虚函数指针--子类虚函数指针初始化 #include<iostream> using namespace std; /* 多态的实现原理(有自己猜想部分) 基础知识: 类 ...

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

    前言 上一篇文章匹夫通过CIL代码简析了一下C#函数调用的话题.虽然点击进来的童鞋并不如匹夫预料的那么多,但也还是有一些挺有质量的来自园友的回复.这不,就有一个园友提出了这样一个代码,这段代码如果被编 ...

  6. C++多态、虚函数、纯虚函数、抽象类、虚基类

    一.C++多态 C++的多态包括静态多态和动态多态.静态多态包括函数重载和泛型编程,动态多态包括虚函数.静态多态是指在编译期间就可以确定,动态多态是指在程序运行时才能确定. 二.虚函数 1.虚函数为类 ...

  7. c++ 虚函数和纯虚函数

    在你设计一个基类的时候,如果发现一个函数需要在派生类里有不同的表现,那么它就应该是虚的.从设计的角度讲,出现在基类中的虚函数是接口,出现在派生类中的虚函数是接口的具体实现.通过这样的方法,就可以将对象 ...

  8. C++ - 虚基类、虚函数与纯虚函数

    虚基类       在说明其作用前先看一段代码 class A{public:    int iValue;}; class B:public A{public:    void bPrintf(){ ...

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

    也许之前我很少写代码,更很少写面向对象的代码,即使有写多半也很容易写回到面向过程的老路上去.在写面向过程的代码的时候,根本不管什么函数重载和覆盖,想到要什么功能就变得法子的换个函数名字,心里想想:反正 ...

随机推荐

  1. C#利用反射机制创建对象

    "反射"其实就是利用程序集的元数据信息. 反射可以有很多方法,编写程序时请先导入 System.Reflection 命名空间. 1.假设你要反射一个 DLL 中的类,并且没有引用 ...

  2. Mysql索引的类型和优缺点

    索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针.注:[1]索引不是万能的!索引可以加快数据检索操作,但会使数据修改操作变慢.每修改数据 ...

  3. javax mail网址

    http://www.oracle.com/technetwork/java/javamail/faq/index.html#tomcatconfig

  4. GreenDao介绍

    GreenDao介绍 greenDAO 是一个将对象映射到 SQLite 数据库中的轻量且快速的 ORM 解决方案 何为ORM? ORM(Object/Relation Mapping): 对象/关系 ...

  5. 解析xml并且导入mysql

    https://www.zhihu.com/question/28139319 解析超过500G的xml 写入mysql,如何尽快写入? 解析超过500G的xml 写入mysql,如何尽快写入? 现在 ...

  6. mariadb:InnoDB: Error: log file ./ib_logfile0 is of different size 0 5242880 bytes

    mariadb 启动中 InnoDB: Error: log file ./ib_logfile0 is of different size 0 起因:线上正在运行的系统,因为需要调整性能,变更了my ...

  7. React Native for Android 学习笔记

    C:\Users\Vic Lee\AwesomeProject>react-native run-android Starting JS server... Running D:\Android ...

  8. html5悬浮球效果

    自己想做一个自己的网站,觉得自适应的效果会好一点,但是放到手机端的话,菜单显示是个问题.所以自己试着写了一个悬浮球菜单的效果. 好了,上代码. 这里有四个文件要用: jqurey.js//因为基于jq ...

  9. 剑指offer三: 斐波拉契数列

    斐波拉契数列是指这样一个数列: F(1)=1; F(2)=1; F(n)=F(n-1)+F(n); public class Solution { public int Fibonacci(int n ...

  10. html在一个页面显示另一个页面的部分内容

    老板今天让在网站上面显示实时监控画面,研究了一早,找了个简单的方法 先把监控分享在网上(我使用的海康威视摄像头,分享到萤石直播http://square.ys7.com/square/index.js ...