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

大家对虚表并不陌生,都知道每个含有虚函数的类对象都有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

posted @ 2018-08-31 22:46  findumars  Views(375)  Comments(0Edit  收藏  举报