zoukankan      html  css  js  c++  java
  • 派生类地址比基类地址少4(子类与基类指针强行转换的时候,值居然会发生变化,不知道Delphi BCB是不是也这样) good

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

                                                            

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

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

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

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

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

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

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

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

    --------------------------------------------------------

    补充:一开始我没看懂这篇文章。后来才发现CBase没有虚函数,也就没有虚指针,所以它的实例头地址只能从自己的第一个成员变量开始。但是子类有,却又不能破坏原来的类结构,于是在它上面额外加了一个地址,用来保存虚指针!Oh, my god...

    亲测,如果把CBase的func加上virtual,运行结果就变成了:

    4398000 4398000 (两者地址值完全一致了)
    4398004 4398008 4398004

  • 相关阅读:
    完美兼容的纯CSS下拉菜单
    ASP.Net分页控件发布(转)
    ASP.NET(C#)FileUpload实现上传限定类型和大小的文件到服务器
    完美的ASP.NET页面分页控件
    Asp.net上传图片同时生成缩略图和水印图
    狗狗约瑟夫环(链表)
    丹叔链表
    囧囧出的题……他自己都没过(一元多项式之和)
    More is better
    最短路
  • 原文地址:https://www.cnblogs.com/findumars/p/5615716.html
Copyright © 2011-2022 走看看