C++虚函数表实例分析

多态是C++面向对象程序设计的一个重要特性。以前看到虚函数觉得很神奇,为什么就能实现多态了呢。最初的时候曾设想,要实现运行时多态,应该让对象的某个部分始终指向一个固定的地址,子类继承的时候,就修改这个地址的内容。这样,父类和子类都是到同一个固定地址去读取内容,在运行时就能表现不同行为。

在看了《深度探索c++对象模型》之后,发现思路是类似的。在对象中,有一个指针指向一张虚函数表,里面按照次序存放了每一个虚函数,当子类继承的时候,即到虚函数表的指定位置去修改函数地址。当我们通过父类指针来操作一个子类的时候,调用虚函数,都是通过虚函数表+固定的偏移,这样运行期多态便实现了。

在深度《深度》这本书中,虚函数表大多放在了对象的末端,不知道现在的编译器是什么样的,因此本文就来实际做个实验测试一下。

实验环境:VC2013 Express

代码如下:

class Parent {
public:
  int parent;
  Parent() : parent(10) {}
  virtual void a() { cout << "Parent::a()" << endl; }
  virtual void b() { cout << "Parent::b()" << endl; }
  virtual void c() { cout << "Parent::c()" << endl; }
};
class Child : public Parent {
public:
  int child;
  Child() :child(100) {}
  virtual void a() { cout << "Child::a()" << endl; }
  virtual void b_child() { cout << "Child::b_child()" << endl; }
  virtual void c_child() { cout << "Child::c_child()" << endl; }
};
class GrandChild : public Child{
public:
  int grandchild;
  GrandChild() :grandchild(1000) {}
  virtual void a() { cout << "GrandChild::a()" << endl; }
  virtual void b_child() { cout << "GrandChild::b_child()" << endl; }
  virtual void c_grandchild() { cout << "GrandChild::c_grandchild()" << endl; }
};
int main()
{
  typedef void(*func)();
  GrandChild grandchild;
  int **vtable = (int **)&grandchild;
  for (int i = 0; (func)vtable[0][i] != nullptr; i++)
  {
    auto pfunc = (func)vtable[0][i];
    cout << "  ["<<i<<"] ";
    pfunc();
  }
  return 0;
}

结果显示如下图所示:

确实,虚函数表指针在对象起始处,并看到了对应项被覆盖。

(0)

相关推荐

  • 解析C++编程中virtual声明的虚函数以及单个继承

    虚函数 虚函数是应在派生类中重新定义的成员函数. 当使用指针或对基类的引用来引用派生的类对象时,可以为该对象调用虚函数并执行该函数的派生类版本. 虚函数确保为该对象调用正确的函数,这与用于进行函数调用的表达式无关. 假定基类包含声明为 virtual 的函数,并且派生类定义了相同的函数. 为派生类的对象调用派生类中的函数,即使它是使用指针或对基类的引用来调用的. 以下示例显示了一个基类,它提供了 PrintBalance 函数和两个派生类的实现 // deriv_VirtualFunctions

  • 浅谈C++基类的析构函数为虚函数

    1.原因: 在实现多态时, 当用基类指针操作派生类, 在析构时候防止只析构基类而不析构派生类. 2.例子: (1). #include<iostream> using namespace std; class Base{ public: Base() {}; ~Base() {cout << "Output from the destructor of class Base!" << endl;}; void DoSomething() { cout

  • 深入解析C++中的虚函数与多态

    1.C++中的虚函数C++中的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数.这种技术可以让父类的指针有"多种形态",这是一种泛型技术.所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法.比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议. 对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Tab

  • C++虚函数及虚函数表简析

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

  • C++虚函数的实现机制分析

    本文针对C++的虚函数的实现机制进行较为深入的分析,具体如下: 1.简单地说,虚函数是通过虚函数表实现的.那么,什么是虚函数表呢? 事实上,如果一个类中含有虚函数,则系统会为这个类分配一个指针成员指向一张虚函数表(vtbl),表中每一项指向一个虚函数的地址,实现上就是一个函数指针的数组. 例如下面这个例子: class Parent { public: virtual void foo1() { } virtual void foo1() { } void foo1(); }; class Ch

  • C++之普通成员函数、虚函数以及纯虚函数的区别与用法要点

    普通成员函数是静态编译的,没有运行时多态,只会根据指针或引用的"字面值"类对象,调用自己的普通函数:虚函数为了重载和多态的需要,在基类中定义的,即便定义为空:纯虚函数是在基类中声明的虚函数,它可以再基类中有定义,且派生类必须定义自己的实现方法. 假设我们有三个类Person.Teacher.Student它们之间的关系如下: 类的关系图 普通成员函数 [Demo1] 根据这个类图,我们有下面的代码实现 #ifndef __OBJEDT_H__ #define __OBJEDT_H__

  • c++中虚函数和纯虚函数的作用与区别

    虚函数为了重载和多态的需要,在基类中是有定义的,即便定义是空,所以子类中可以重写也可以不写基类中的此函数! 纯虚函数在基类中是没有定义的,必须在子类中加以实现,很像java中的接口函数! 虚函数 引入原因:为了方便使用多态特性,我们常常需要在基类中定义虚函数. class Cman { public: virtual void Eat(){--}; void Move(); private: }; class CChild : public CMan { public: virtual void

  • 深入解析C++编程中的纯虚函数和抽象类

    C++纯虚函数详解 有时在基类中将某一成员函数定为虚函数,并不是基类本身的要求,而是考虑到派生类的需要,在基类中预留了一个函数名,具体功能留给派生类根据需要去定义. 纯虚函数是在声明虚函数时被"初始化"为0的函数.声明纯虚函数的一般形式是 virtual 函数类型 函数名 (参数表列) = 0; 关于纯虚函数需要注意的几点: 纯虚函数没有函数体: 最后面的"=0"并不表示函数返回值为0,它只起形式上的作用,告诉编译系统"这是纯虚函数"; 这是一个

  • 实例讲解C++编程中的虚函数与虚基类

    虚函数 ① #include "stdafx.h" #include <iostream> using namespace std; class B0//基类B0声明 { public: void display(){cout<<"B0::display()"<<endl;}//公有成员函数 }; class B1: public B0//公有派生类B1声明 { public: void display(){cout<<

  • 深入探讨C++父类子类中虚函数的应用

    构造函数不能是虚函数,因为在调用构造函数创建对象时,构造函数必须是确定的,所以构造函数不能是虚函数.析构函数可以是虚函数. 1.父类Father.h: 复制代码 代码如下: #pragma onceclass Father{public: Father(void); virtual ~Father(void); virtual int getCount();public: int count;}; Father.cpp 复制代码 代码如下: #include "StdAfx.h"#in

  • C++ COM编程之接口背后的虚函数表

    前言 学习C++的人,肯定都知道多态机制:多态就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数.对于多态机制是如何实现的,你有没有想过呢?而COM中的接口就将这一机制运用到了极致,所以,不知道多态机制的人,是永运无法明白COM的.所以,在总结COM时,是非常有必要专门总结一下C++的多态机制是如何实现的. 多态 什么是多态?上面也说了,多态就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数.现在通过代码,让大家切身的体会一下多态: 复制代

  • 详解C++编程中的虚函数

    我们知道,在同一类中是不能定义两个名字相同.参数个数和类型都相同的函数的,否则就是"重复定义".但是在类的继承层次结构中,在不同的层次中可以出现名字相同.参数个数和类型都相同而功能不同的函数. 人们提出这样的设想,能否用同一个调用形式,既能调用派生类又能调用基类的同名函数.在程序中不是通过不同的对象名去调用不同派生层次中的同名函数,而是通过指针调用它们.例如,用同一个语句"pt->display( );"可以调用不同派生层次中的display函数,只需在调用前

  • 详解C++中基类与派生类的转换以及虚基类

    C++基类与派生类的转换 在公用继承.私有继承和保护继承中,只有公用继承能较好地保留基类的特征,它保留了除构造函数和析构函数以外的基类所有成员,基类的公用或保护成员的访问权限在派生类中全部都按原样保留下来了,在派生类外可以调用基类的公用成员函数访问基类的私有成员.因此,公用派生类具有基类的全部功能,所有基类能够实现的功能, 公用派生类都能实现.而非公用派生类(私有或保护派生类)不能实现基类的全部功能(例如在派生类外不能调用基类的公用成员函数访问基类的私有成员).因此,只有公用派生类才是基类真正的

随机推荐