派生类地址比基类地址少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,为什么呢?为了加深理解,我做了一个简单的测试代码:
- #include <stdio.h>class CBase {
- public:
- CBase() {}
- void func()
- {
- printf("base\n");
- }
- };
- class CDerived : public CBase {
- public:
- CDerived() {}
- virtual void func1()
- {
- printf("derived\n");
- }
- };
- void main()
- {
- CBase *pBase = new CDerived();
- pBase->func();
- CDerived *pDerived = (CDerived *)pBase;
- printf("%d %d\n", pDerived, pBase);
- pDerived->func();
- CBase *pBase1 = new CBase();
- pBase1->func();
- CDerived *pDerived1 = (CDerived *)pBase1;
- printf("%d %d\n", pDerived1, pBase1);
- pDerived1->func();
- }
下面是输出的结果,从结果可以看出派生类指针指向基类指针,指针地址会加4,基类指针指向派生类时,指针地址会减4。
base
200672 200676
derived
base
200740 200744
Press any key to continue
下面我们看看派生类对象和基类对象的内存是如何组织的,我们在上例的基础上引入2个变量,代码如下:
- #include <stdio.h>class CBase {
- public:
- CBase() {}
- void func()
- {
- printf("base\n");
- }
- int a;
- };
- class CDerived : public CBase {
- public:
- CDerived() {}
- virtual void func1()
- {
- printf("derived\n");
- }
- int b;
- };
- void main()
- {
- CBase *pBase = new CDerived();
- CDerived *pDerived = (CDerived *)pBase;
- printf("%d %d\n", pDerived, pBase);
- printf("%d %d %d\n", &pDerived->a, &pDerived->b, &pBase->a);
- }
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
派生类地址比基类地址少4(子类与基类指针强行转换的时候,值居然会发生变化,不知道Delphi BCB是不是也这样) good的更多相关文章
- C#中子类对基类方法的继承、重写和隐藏
提起子类.基类和方法继承这些概念,肯定大家都非常熟悉.毕竟,作为一门支持OOP的语言,掌握子类.基类是学习C#的基础.不过,这些概念虽然简单,但是也有一些初学者可能会遇到的坑,我们一起看看吧. 子 ...
- Java 工具类 IpUtil - 获取本机所有 IP 地址,LocalHost 对应地址 IP
Java 工具类 IpUtil - 获取本机所有 IP 地址,LocalHost 对应地址 IP IP 工具类 源代码: /** * <p> * * @author XiaoPengwei ...
- Effective Objective-C 2.0 — 第二条:类的头文件中尽量少引入其他头文件
第二条:类的头文件中尽量少引入其他头文件 使用向前声明(forward declaring) @class EOCEmployer 1, 将引入头文件的实际尽量延后,只在确有需要时才引入,这样就可以减 ...
- UI(UGUI)框架(二)-------------UIManager单例模式与开发BasePanel面板基类/UIManage统一管理UI面板的实例化/开发字典扩展类
UIManage单实例: /// 单例模式的核心 /// 1,定义一个静态的对象 在外界访问 在内部构造 /// 2,构造方法私有化 private static UIManager _instanc ...
- 在类的头文件里尽量少引入其它头文件 <<Effective Objective-C>>
与C 和C++ 一样,Objective-C 也使用"头文件"(header file) 与"实现文件"(implementation file)来区隔代码.用 ...
- 介绍了如何取成员函数的地址以及调用该地址:C++
摘要:介绍了如何取成员函数的地址以及调用该地址. 关键字:C++成员函数 this指针 调用约定 一.成员函数指针的用法 在C++中,成员函数的指针是个比较特殊的东西.对普通的函数指针来说,可以视为一 ...
- IP地址、子网掩码和地址分类
http://blog.csdn.net/bluishglc/article/details/47909593?utm_source=tuicool&utm_medium=referral 实 ...
- Java常用类归纳(Object、System、Properties、包装类和工具类等等)
Object类 Object 是类层次结构的根类.每个类都使用 Object 作为超类,所有对象(包括数组)都实现这个类的方法.了解Object的方法是很有必要的. protected Object ...
- 对象布局已知时 C++ 对象指针的转换时地址调整
在我调试和研究 netscape 系浏览器插件开发时,注意到了这个问题.即,在对象布局已知(即对象之间具有继承关系)时,不同类型对象的指针进行转换(不管是隐式的从下向上转换,还是强制的从上到下转换)时 ...
随机推荐
- 经典:十步完全理解 SQL
经典:十步完全理解 SQL 来源:伯乐在线 链接:http://blog.jobbole.com/55086/ 很多程序员视 SQL 为洪水猛兽.SQL 是一种为数不多的声明性语言,它的运行方式完 ...
- EC读书笔记系列之15:条款32、33、34
条款32 确保你的public继承塑模出is-a关系 记住: ★public继承意味着is-a.适用于base class身上的每一件事情一定也适用于derived class身上,∵每一个deriv ...
- Git 系列(三):建立你的第一个 Git 仓库
现在是时候学习怎样创建你自己的 Git 仓库了,还有怎样增加文件和完成提交. 在本系列前面的文章中,你已经学习了怎样作为一个最终用户与 Git 进行交互:你就像一个漫无目的的流浪者一样偶然发现了一个开 ...
- 标准C编程-笔记全集
C语言的基本概念 编写一个简单的C程序,后缀名保存为c(本次文件名为a.c) gcc:对c程序进行编译和连接:gcc a.c ./a.out:运行程序,输出程序的结果:其中a是c程序的文件名 说明:其 ...
- Yahoo团队经验:网站性能优化的34条黄金法则
Yahoo团队总结的关于网站性能优化的经验,非常有参考价值.英文原文:http://developer.yahoo.com/performance/rules.html 1.尽量减少HTTP请求次数 ...
- [问题解决] ubuntu server12.04 按ctrl+alt+F1没用
错误: ubuntu server12.04 想从图形化界面变成命令行界面时候,按ctrl+alt+F1没用 发生场景: 虚拟机下的ubuntu server12.04 解决方案: 因为ctrl+al ...
- delphi 关于命名
请告别 TMyXXX 的命名方法吧... 程序名: Demo.exe 窗体:TFrmDemo ,窗体文件 uFrmDemo.Pas DataModule: TDMDemo, 窗体文件 uDMDemo. ...
- Servlet的运行方式
通常我们运行servlet需要在web.xml配置文件中,注册我们写好的servlet以及其对应的访问路径. 在学习web开发中,有一种不需要配置便可以直接对servlet进行配置的方式,在web.x ...
- Android_Intent意图详解
本博文为子墨原创,转载请注明出处! http://blog.csdn.net/zimo2013/article/details/11863857 1.Intent作用 Intent是一个将要执行的动作 ...
- 在Prefetcher中取消robots.txt的限制
Robots.txt是一种专门用于搜索引擎网络爬虫的文件,当构造一个网站时,如果作者希望该网站的内容被搜索引擎收录,就可以在网站中创建一个纯文本文件robots.txt,在这个文件中,声明该网站不想被 ...