C++多重继承下的指针类型转换

在C++中,指针的类型转换是经常发生的事情,比如将派生类指针转换为基类指针,将基类指针转换为派生类指针。指针的本质其实就是一个整数,用以记录进程虚拟内存空间中的地址编号,而指针的类型决定了编译器对其指向的内存空间的解释方式。

基于上面的理解,我们似乎可以得出一个结论,C++中对指针进行类型转换,不会改变指针的值,只会改变指针的类型(即改变编译器对该指针指向内存的解释方式),但是这个结论在C++多重继承下是 不成立的。

看下面一段代码:

 1 #include <iostream>
 2 using namespace std;
 3
 4 class CBaseA
 5 {
 6 public:
 7     char m_A[32];
 8 };
 9
10 class CBaseB
11 {
12 public:
13     char m_B[64];
14 };
15
16 class CDerive : public CBaseA, public CBaseB
17 {
18 public:
19     char m_D[128];
20 };
21
22
23 int main()
24 {
25     auto pD = new CDerive;
26     auto pA = (CBaseA *)pD;
27     auto pB = (CBaseB *)pD;
28
29     cout << pA << '\n' << pB << '\n' << pD << endl;
30     cout << (pD == pB) << endl;
31 }

这段代码的输出是:

0x9f1080
0x9f10a0
0x9f1080
1

可以看出,指向同一个堆上new出来的内存指针,在经过类型转换之后,其值会发生改变。究其原因,要从C++中多重继承的内存布局说起。

new CDerive;执行之后,生成的内存布局如下:

 

同时我们注意到,pB与pD的指针差值正好是CBaseA占用的内存大小32字节,而pA与pD都指向了同一段地址。这是因为,将一个派生类的指针转换成某一个基类指针,编译器会将指针的值偏移到该基类在对象内存中的起始位置。

可是为什么C++要这样设计呢?

试想,沿用上面的场景,如果pB和pA都指向对象的首地址,那么使用pB指针来定位CBaseB的成员变量m_B时,编译器应该将pB指针偏移32个字节,从而跳过CBaseA的内存部分。而pB指针如果是这样产生的auto pB = new CBaseB;,那么使用pB指针来定位CBaseB的成员变量m_B时,偏移量应该为0。

关键在于对于一个指针而言,编译器不关心也无法知道该指针的来源(一种极端情况,指针是从其他模块传递过来的),而只是把它视为一个有指针类型的整数。所以对于CBaseB类型的指针,取CBaseB的成员变量m_B时,偏移量应该通通为0,这是通过CBaseB的类声明就可以统一决策的事情。

说到这里,pD和pB的指针地址为什么不一样大家应该清楚了,可是为什么下面的代码输出是1呢?

cout << (pD == pB) << endl;

输出1表示pD和pB是相等的,而刚刚我们才说明了,pD和pB的地址是相差了32个字节的。

其实这也是编译器为大家屏蔽了这种指针的差异,当编译器发现一个指向派生类的指针和指向其某个基类的指针进行==运算时,会自动将指针做隐式类型提升已屏蔽多重继承带来的指针差异。因为两个指针做比较,目的通常是判断两个指针是否指向了同一个内存对象实例,在上面的场景中,pD和pB虽然指针值不等,但是他们确确实实都指向了同一个内存对象(即new CDerive;产生的内存对象 ),所以编译器又在此处插了一脚,让我们可以安享==运算的上层语义。

 

 

from:http://www.cnblogs.com/itZhy/archive/2012/10/08/2713367.html

时间: 2024-11-02 02:49:14

C++多重继承下的指针类型转换的相关文章

c++问题-C++指针类型转换问题,以及字节问题

问题描述 C++指针类型转换问题,以及字节问题 为什么说short int 类型如97的高字节为0,而A却不为0,为什么short int类指针可以转换其他类型.

C中void指针类型转换到C#的问题.

问题描述 我用C#调用C编译的dll中有这样一个函数,函数大概的功能就是把数据保存到buf缓冲区中:intretrieve(intscanno,void*buf); 在C中是通过先定义一个结构体再调用这个函数的:#defineCOUNT_DIMENSION_MAX256typedefstructtagVECTOR_st{intdimension;doublevector[COUNT_DIMENSION_MAX];}VECTOR_st;structtagSample_st{intID;VECTOR

c++ 由类型转换引起的指针偏移

由于转载了另外的转载,且原文暂时未找到,此处无法列出原文地址. 在C语言中,如果对一个指针做类型转换,不会改变这个指针的值,改变的只是对指针的解释方式.但是在C++中,由于一些特性的引入,在对指针做类型转换时,编译器有时不得不对指针做一个偏移,以支持这些特性.下面将具体讨论这些情况.1. 由虚函数引起的指针偏移通常在有虚函数的类中,编译器会安插一个vptr,但是对vptr的位置C++语言并未做出明确的规定,就目前的实现来看,有的编译器将vptr放在类的开头,如microsoft的c++编译器,而

深入解析C++中的动态类型转换与静态类型转换运算符_C 语言

dynamic_cast 运算符将操作数 expression 转换成类型为type-id 的对象. 语法 dynamic_cast < type-id > ( expression ) 备注 type-id 必须是一个指针或引用到以前已定义的类类型的引用或"指向 void 的指针".如果 type-id 是指针,则expression 的类型必须是指针,如果 type-id 是引用,则为左值. 有关静态和动态强制转换之间区别的描述,以及各在什么情况下适合使用,请参见 st

Swift 中的指针使用

Apple 期望在 Swift 中指针能够尽量减少登场几率,因此在 Swift 中指针被映射为了一个泛型类型,并且还比较抽象.这在一定程度上造成了在 Swift 中指针使用的困难,特别是对那些并不熟悉指针,也没有多少指针操作经验的开发者 (包括我自己也是) 来说,在 Swift 中使用指针确实是一个挑战.在这篇文章里,我希望能从最基本的使用开始,总结一下在 Swift 中使用指针的一些常见方式和场景.这篇文章假定你至少知道指针是什么,如果对指针本身的概念不太清楚的话,可以先看看这篇五分钟 C 指

C的指针

指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址. 要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的 类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区.让我们分别说明. 先声明几个指针放着做例子: 例一: (1)int*ptr; (2)char*ptr; (3)int**ptr; (4)int(*ptr)[3]; (5)int*(*ptr)[4]; 如果看不懂后几个例子的话,请参阅我前段时间贴出的文章<<如何理解c和c ++的复杂类型声明&g

C++中指针的数据类型和运算相关知识小结_C 语言

C++有关指针的数据类型和指针运算的小结 前面已用过一些指针运算(如p++,p+i等),现在把全部的指针运算列出如下. 1) 指针变量加/减 一个整数 例如:p++,p--,p+i,p-i,p+-i,p-=i等. C++规定,一个指针变量加/减一个整数是将该指针变量的原值(是一个地址)和它指向的变量所占用的内存单元字节数相加或相减.如p+i代表这样的地址计算:p+i*d,d为p所指向的变量单元所占用的字节数.这样才能保证p+i指向p下面的第i个元素. 2) 指针变量赋值 将一个变量地址赋给一个指

Swift中的指针操作和使用详细介绍_Swift

Apple期望在Swift中指针能够尽量减少登场几率,因此在Swift中指针被映射为了一个泛型类型,并且还比较抽象.这在一定程度上造成了在Swift中指针使用的困难,特别是对那些并不熟悉指针,也没有多少指针操作经验的开发者(包括我自己也是)来说,在Swift中使用指针确实是一个挑战.在这篇文章里,我希望能从最基本的使用开始,总结一下在Swift中使用指针的一些常见方式和场景.这篇文章假定你至少知道指针是什么,如果对指针本身的概念不太清楚的话,可以先看看这篇五分钟C指针教程(或者它的中文版本),应

C/C++指针小结_C 语言

第一章.指针的概念 指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址.要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区.让我们分别说明. 先声明几个指针放着做例子: 例一: (1)int *ptr; (2)char *ptr; (3)int **ptr; (4)int (*ptr)[3]; (5)int *(*ptr)[4]; 如果看不懂后几个例子的话,请参阅我前段时间贴出的文章<<如何理解c和