对C++虚函数、虚函数表的简单理解

一、虚函数的作用

以一个通用的图形类来了解虚函数的定义,代码如下:

#include "stdafx.h"
#include <iostream>
using namespace  std;

class Graph
{
protected:
    double x;
    double y;
public:
    Graph(double x,double y)
    {
        this->x=x;
        this->y=y;
    }
    virtual void showArea()
    {
        cout<<"计算图形面积"<<endl;
    }
};

class Rectangle:public Graph
{
public:
    Rectangle(double x,double y):Graph(x,y){};
    virtual void showArea()
    {
        cout<<"矩形面积为:"<<x*y<<endl;
    }
};

class Triangle:public Graph
{
public:
    Triangle(double d,double h):Graph(d,h){};
    virtual void showArea()
    {
        cout<<"三角形面积为:"<<x*y*0.5<<endl;
    }
};

class Circle:public Graph
{
public:
    Circle(double r):Graph(r,r){};
    virtual void showArea()
    {
        cout<<"圆形面积为:"<<3.14*x*y<<endl;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Graph *graph;

    Rectangle rectangle(10,5);
    graph=&rectangle;
    graph->showArea();

    Triangle triangle(5,2.4);
    graph=&triangle;
    graph->showArea();

    Circle circle(2);
    graph=&circle;
    graph->showArea();
    return 0;
}

输出如下,

该代码的作用是,设计不同的类去处理不同的图形,所有类继承于同一个Graph类。但是每个图形类都有一个相同的函数showArea。如果有需求是输出多个图形的面积,如果没有虚函数,就需要给每一种图形设计一个数组,然后每种图形分别处理。但是有了虚函数后,不管是什么图形,都只要设计一个Graph数组,然后调用每个成员的showArea函数即可。

示例代码:

Graph* gArray[3];
    gArray[0] = &rectangle;
    gArray[1] = &triangle;
    gArray[2] = &circle;
    for (int i = 0;i < 3;i++)
    {
        gArray[i]->showArea();
    }

输出结果:

为什么会有这样的效果呢?原因就是有虚函数表。

二、虚函数表

调试上面的图形类代码,用IDE里的调试工具查看每个类对象;

每个对象(事实上,所有相同的类对象,都共用一个虚函数表)都有一个__vfptr成员数组,这个__vfptr就是虚函数表,偏移在基类和继承类中都是一样的,所以虚函数表可以在基类中访问到。

但是仔细查看上面的图,可以看到,每个对象的虚函数表数组都是指向的不同的地址,分别指向自己类中的虚函数。

所以如果指定graph->showArea()

调用的函数是graph->__vfptr[0]()。

这样就保证每个子类都调用的都是自己对应的虚函数了。

时间: 2024-10-01 04:59:53

对C++虚函数、虚函数表的简单理解的相关文章

C++学习 - 虚表,虚函数,虚函数表指针学习笔记

虚函数 虚函数就是用virtual来修饰的函数.虚函数是实现C++多态的基础. 虚表 每个类都会为自己类的虚函数创建一个表,来存放类内部的虚函数成员. 虚函数表指针 每个类在构造函数里面进行虚表和虚表指针的初始化. 下面看一段代码: // // main.cpp // VirtualTable // // Created by Alps on 15/4/14. // Copyright (c) 2015年 chen. All rights reserved. // #include <iostr

C++ - 类的虚函数\虚继承所占的空间

类的虚函数\虚继承所占的空间 本文地址: http://blog.csdn.net/caroline_wendy/article/details/24236469 char占用一个字节, 但不满足4的倍数时, 会进行数据对齐, 使用4的倍数的字节(byte). GCC: GCC中, 不管是虚函数还是虚继承, 都须要将指针存储在虚函数表(virtual function table), 占用4个字节. 继承会继承基类的数据, 和虚函数表, 即继承基类的空间. 代码: /* * test.cpp *

C++中的 虚函数 纯虚函数 虚基类(virtual)

前言:需要了解三者的区别,必须要掌握多态的三个必要条件: 继承 重载 父类指针指向子类对象. 虚函数 纯虚函数 虚基类三者区别 1.虚函数是用于多态中virtual修饰父类函数,确保父类指针调用子类对象时,运行子类函数的. 2.纯虚函数是用来定义接口的,也就是基类中定义一个纯虚函数,基类不用实现,让子类来实现. 3.虚基类是用来在多继承中,比如菱形继承中,如果两个父类继承自同一个类,就只实例化一个父类 ①虚函数第一个是没有使用多态(只用继承)的一般实现方式: class A { public:

虚函数&amp;&amp;虚继承

如果说没有虚函数的虚继承只是一个噩梦的话,那么这里就是真正的炼狱.这个C++中最复杂的继承层次在VS上的实现其实我没有完全理解,摸爬滚打了一番也算得出了微软的实现方法吧,至于一些刁钻的实现方式我也想不到什么理由来解释它,也只算是知其然不知其所以然吧. 虚.实基类都没有虚函数 这种情况也还算比较简单.因为虚函数表指针一定是会放在最开始的,所以根据猜测也可以知道其大概布局情况.看下面一个简单的例子 #pragma pack(8) class F1{ public: int f1; F1():f1(0

C++ 虚函数 虚继承

C++ 的虚函数主要就是 实现多态机制,主要是通过一个虚函数表,知道具体的子类函数还是父类函数的地址,在执行. 先来看一个父类: 父类的虚函数表: 表中的最后一段区域 . 表示虚函数表的结束标志,若此处为1 还有下一个虚表 为0 虚表结束. 一般继承(没有覆盖的情况) 没有覆盖的情况下继承,子类的虚函数地址 存放在父类的后面: 继承(存在覆盖):多态 此时的虚函数表: 可以看到子类覆盖父类的函数f(); 所以当 Base * p = new Derived; p->f(); 在此虚函数表中,调用

深入理解C++虚函数表

虚函数表是C++类中存放虚函数的一张表,理解虚函数表对于理解多态很重要. 本次使用的编译器是VS2013,为了简化操作,不用去操作函数指针,我使用到了VS的CL编译选项来查看类的内存布局. CL使用方法: (1)在开始菜单中的vs目录下打开"Visual Studio Tools"目录,找到"VS2013 开发人员命令提示",打开它: (2)将你要编译的文件放到该命令行对应的文件夹中. (3)输入cl "文件名" /d1reportSingleC

C++虚函数表小记

最近在读C++ primer plus,看到了关于虚函数表的部分,觉得应该记录一下. C++只是规定了虚函数应该如何做,但是具体的实现是编译器来负责的.通常编译器是通过向对象添加一个隐藏的成员来操作虚函数,这个隐藏的成员是一个指向数组的指针,这个数组的内容是方法的地址.这个数组通常叫做“虚表”(virtual function table).这个虚表托管的是这个类所有虚函数的地址.例如,一个基类的对象包含一个指向这个类所有虚函数地址的数组的指针.如果派生类重写了虚函数,则派生类对象的虚表中相应的

c++ 深入理解虚函数

为什么使用虚函数?什么是虚函数?虚函数是为了解决什么问题? 面向对象的三大特征: 封装 多态 继承 普通虚函数 虚析构函数 纯虚函数 抽象类 接口类 隐藏 vs 覆盖 隐藏与覆盖之间的关系 早绑定和晚绑定 虚函数表 什么是多态? 相同对象收到不同消息或不同对象收到相同消息时产生的不同的动作. 静态多态 vs 动态多态 [-:>静态多态也叫做早绑定 class Rect //矩形类 { public: int calcArea(int width); int calcArea(int width,

GeekBand-secondweek-c++的多态和虚函数

多态与虚函数 13章的简单继承只是实现了对已有对象的实现的重定义和直接调用,但是向上映射导致的对象切割仍然是个缺陷: 1.延续13章的向上映射 简单继承中,派生类重定义了基类的成员函数,此时,向上映射的结果是很明显的,它使用了基类实现的函数版本,这显然并不是我们想要的效果:为什么会有这样的结果发生,我们先探讨几个问题: 函数调用绑定:函数调用确定目标函数体称为捆绑,编译期绑定称为早绑定,上面的问题就是早绑定引起的,因为编译器只知道基类对象的类型,调用函数也会绑定基类实现的函数版本,要解决这一问题