派生类地址比基类地址少4(子类与基类指针强行转换的时候,值居然会发生变化,不知道Delphi BCB是不是也这样) good
2016-06-25 04:40
597 查看
大家对虚表并不陌生,都知道每个含有虚函数的类对象都有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
#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个变量,代码如下:
[cpp] view plain copy
#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
CTree中包括两个成员变量,QList <CTree *> childList;树中有多少个孩子节点;CTree *parent;当前树节点的父亲节点,程序中我大量使用CTree *pTree指针指向CSamplerTree、CMSamplerTree、CASamplerTree ,从而达到统一处理的目的,从而使代码很简洁,复用性高。但是谁曾想到,程序一运行就会崩溃,通过调试发现,CSamplerTree、CMSamplerTree、CASamplerTree的指针当指向CTree的指针时,地址均加了4,为什么呢?为了加深理解,我做了一个简单的测试代码:
[cpp] view plain copy
#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个变量,代码如下:
[cpp] view plain copy
#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
相关文章推荐
- delphi 读取编译的version信息
- Delphi常用操作说明
- Delphi XE不生成__history目录
- Delphi的对象注销方法Destroy和free的区别
- 问题-Delphi7中JSON遍历节点不支持使用IN处理方法?
- 理解windows消息机制-delphi消息机制学习笔记。
- delphi启动外部程序执行结束
- delphi datasnap 心跳包
- delphi之IOCP学习(一)
- Delphi中window消息截获的实现方式(2)
- Delphi中window消息截获的实现方式(1)
- Delphi Dll 消息处理
- Delphi中StrToDateTime函数TFormatSettings参数的使用
- Delphi XE6 利用FastMM4检测内存泄漏
- delphi 判断WIN8 , WIN8.1 , WIN10 系统版本
- Delphi 文件夹遍历所有文件包括子文件文件,并可通过后缀过滤
- Delphi 类型转换函数(有几个函数没见过,FloatToStrF,FloatToText等等)
- Delphi 常用属性说明(超长)
- Delphi程序自删除的几种方法
- Delphi 线程同步技术(转)