大家对虚表并不陌生,都知道每个含有虚函数的类对象都有1个虚指针,但是在现实使用中,却总是因为这而调试半天,才发现原来是虚指针惹的祸。我这几天在调试代码时候也中招了,我的问题是这样的,如下图,CTree是最底层基类(非虚类), CSamplerTree(虚类)派生自CTree,CMSamplerTree,CASamplerTree派生自CSamplerTree,

CTree中包括两个成员变量,QList <CTree *> childList;树中有多少个孩子节点;CTree *parent;当前树节点的父亲节点,程序中我大量使用CTree *pTree指针指向CSamplerTree、CMSamplerTree、CASamplerTree ,从而达到统一处理的目的,从而使代码很简洁,复用性高。但是谁曾想到,程序一运行就会崩溃,通过调试发现,CSamplerTree、CMSamplerTree、CASamplerTree的指针当指向CTree的指针时,地址均加了4,为什么呢?为了加深理解,我做了一个简单的测试代码:

  1.  
    #include <stdio.h>class CBase {
  2.  
    public:
  3.  
    CBase() {}
  4.  
    void func()
  5.  
    {
  6.  
    printf("base\n");
  7.  
    }
  8.  
    };
  9.  
    class CDerived : public CBase {
  10.  
    public:
  11.  
    CDerived() {}
  12.  
    virtual void func1()
  13.  
    {
  14.  
    printf("derived\n");
  15.  
    }
  16.  
    };
  17.  
    void main()
  18.  
    {
  19.  
    CBase *pBase = new CDerived();
  20.  
    pBase->func();
  21.  
    CDerived *pDerived = (CDerived *)pBase;
  22.  
    printf("%d %d\n", pDerived, pBase);
  23.  
    pDerived->func();
  24.  
    CBase *pBase1 = new CBase();
  25.  
    pBase1->func();
  26.  
    CDerived *pDerived1 = (CDerived *)pBase1;
  27.  
    printf("%d %d\n", pDerived1, pBase1);
  28.  
    pDerived1->func();
  29.  
    }

下面是输出的结果,从结果可以看出派生类指针指向基类指针,指针地址会加4,基类指针指向派生类时,指针地址会减4。

base
200672 200676
derived
base
200740 200744
Press any key to continue

下面我们看看派生类对象和基类对象的内存是如何组织的,我们在上例的基础上引入2个变量,代码如下:

  1.  
    #include <stdio.h>class CBase {
  2.  
    public:
  3.  
    CBase() {}
  4.  
    void func()
  5.  
    {
  6.  
    printf("base\n");
  7.  
    }
  8.  
    int a;
  9.  
    };
  10.  
    class CDerived : public CBase {
  11.  
    public:
  12.  
    CDerived() {}
  13.  
    virtual void func1()
  14.  
    {
  15.  
    printf("derived\n");
  16.  
    }
  17.  
    int b;
  18.  
    };
  19.  
    void main()
  20.  
    {
  21.  
    CBase *pBase = new CDerived();
  22.  
    CDerived *pDerived = (CDerived *)pBase;
  23.  
    printf("%d %d\n", pDerived, pBase);
  24.  
    printf("%d %d %d\n", &pDerived->a, &pDerived->b, &pBase->a);
  25.  
    }
  26.  
     

200672 200676
200676 200680 200676
Press any key to continue
从输出结果我们可看出,CDerived对象的起始地址存放的是虚表指针vptr,接下来的是基类的成员变量,接下来再是自身的成员变量。

https://blog.csdn.net/rabinsong/article/details/8923137

派生类地址比基类地址少4(CDerived对象的起始地址存放的是虚表指针vptr,也就是子类的第一项内容。接下来的是基类的成员变量,接下来再是自身的成员变量)的更多相关文章

  1. c++派生类中构造函数和析构函数执行顺序、判断对象类型、抽象类、虚函数

    一. 代码: 1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 #include&l ...

  2. C++ 虚指针、成员变量与类对象的偏移地址

    先给出一段代码实现 #include <iostream> using namespace std; class animal { protected: int age; public: ...

  3. 基类中定义的虚函数在派生类中重新定义时,其函数原型,包括返回类型、函数名、参数个数、参数类型及参数的先后顺序,都必须与基类中的原型完全相同 but------> 可以返回派生类对象的引用或指针

      您查询的关键词是:c++primer习题15.25 以下是该网页在北京时间 2016年07月15日 02:57:08 的快照: 如果打开速度慢,可以尝试快速版:如果想更新或删除快照,可以投诉快照. ...

  4. Effective Objective-C 2.0 — 第二条:类的头文件中尽量少引入其他头文件

    第二条:类的头文件中尽量少引入其他头文件 使用向前声明(forward declaring) @class EOCEmployer 1, 将引入头文件的实际尽量延后,只在确有需要时才引入,这样就可以减 ...

  5. C#多态;父类引用指向子类对象;new和override的区别;new、abstract、virtual、override,sealed关键字区别和使用代码示例;c#类的初始化顺序

    关于父类引用指向子类对象 例如: 有以下2个类 public class Father { public int age = 70; public static string name = " ...

  6. java基础课程笔记 static 主函数 静态工具类 classpath java文档注释 静态代码块 对象初始化过程 设计模式 继承 子父类中的函数 继承中的构造函数 对象转型 多态 封装 抽象类 final 接口 包 jar包

    Static那些事儿 Static关键字 被static修饰的变量成为静态变量(类变量) 作用:是一个修饰符,用于修饰成员(成员变量,成员方法) 1.被static修饰后的成员变量只有一份 2.当成员 ...

  7. Java之面向对象概述,类,构造方法,static,主方法,对象

    一.面向对象概述 面向过程 "面向过程"(Procedure Oriented)是一种以过程为中心的编程思想.这些都是以什么正在发生为主要目标进行编程,不同于面向对象的是谁在受影响 ...

  8. Java学习笔记13---如何理解“子类重写父类方法时,返回值若为类类型,则必须与父类返回值类型相同或为其子类”

    子类重新实现父类的方法称重写:重写时可以修改访问权限修饰符和返回值,方法名和参数类型及个数都不可以修改:仅当返回值为类类型时,重写的方法才可以修改返回值类型,且必须是父类方法返回值的子类:要么就不修改 ...

  9. C++类和对象(一)&&实现offsetof宏&&this指针

    一.目录 1.对象的相关知识 2.类的定义 3.类的实例化 4.类对象模型 5.模拟实现offsetof宏 6.this指针 二.正文 1.对象的相关知识 C语言是面向过程的,关注的是过程,分析求解问 ...

随机推荐

  1. 在量化金融中15个最流行的Python数据分析库

    Python是当今应用最广泛的编程语言之一,以其效率和代码可读性著称.作为一个科学数据的编程语言,Python介于R和java之间,前者主要集中在数据分析和可视化,而后者主要应用于大型应用.这种灵活性 ...

  2. webpack打包css自动添加css3前缀

    为了浏览器的兼容性,有时候我们必须加入-webkit,-ms,-o,-moz这些前缀.目的就是让我们写的页面在每个浏览器中都可以顺利运行. 1.安装 cnpm i postcss-loader aut ...

  3. Android学习——控件ListView的使用

    一.ListView的简单用法 首先新建一个ListViewTest项目,并让Android Studio自动创建好活动.然后修改activity_main.xml中的代码,如下: <?xml ...

  4. poj3281 Dining 最大流(奇妙的构图)

    我是按照图论500题的文档来刷题的,看了这题怎么也不觉得这是最大流的题目.这应该是题目做得太少的缘故. 什么是最大流问题?最大流有什么特点? 最大流的特点我觉得有一下几点: 1.只有一个起点.一个终点 ...

  5. Linux vi命令快操作汇总

    第一部份:一般指令模式可用的按钮說明,游标移动.复制粘贴.搜寻取代等 一.移动游标的方法h 或 向左方向鍵(←) 游标向左移动一个字节j 或 向下方向鍵(↓)   游标向下移动一个字节k 或 向上方向 ...

  6. Kinect安装与配置(openNI2)

    原文链接:http://blog.csdn.net/chenxin_130/article/details/8580636 简介 最近OpenNI2的推出,小斤也要多给博客除除草了,并在闲暇之余做一些 ...

  7. 基于MapReduce的贝叶斯网络算法研究参考文献

    原文链接(系列):http://blog.csdn.net/XuanZuoNuo/article/details/10472219 论文: 加速贝叶斯网络:Accelerating Bayesian ...

  8. __str__和__repr__

    Python 中的 __str__ 与 __repr__ 到底有什么差别   很多时候我们自己编写一个类,在将它的实例在终端上打印或查看的时候,我们往往会看到一个不太满意的结果. 类默认转化的字符串基 ...

  9. 什么是JSON ?

    JSON 实例 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <t ...

  10. NYOJ-1013除法表达式

    除法表达式 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述 给出一个这样的除法表达式:X1/X2/X3/···/Xk,其中Xi是正整数.除法表达式应当按照从左到右的顺 ...