在C/c++中,类型之间的转换是经常被遇到的,在C++中,经常会遇到指针的类型转换,比如将派生类指针转换为基类指针,将基类指针转换为派生类指针。指针的本质其实就是一个数字,用以记录进程虚拟内存空间中的地址编号,而指针的类型决定了编译器对其指向的内存空间的解释方式。
基于上面的理解,我们似乎可以得出一个结论,C++中对指针进行类型转换,不会改变指针的值,只会改变指针的类型(即改变编译器对该指针指向内存的解释方式),但是这个结论在C++多重继承下是 不成立的。
以下程序例子:
- #include <iostream>
- using namespace std;
- class ca{
- char m_a[32];
- };
- class cb{
- char m_b[64];
- };
- class cd : public ca,public cb{
- char m_d[128];
- };
- int main(int argc,char **argv)
- {
- cd *pD = new cd;
- ca *pA = (ca *)pD;
- cb *pB = (cb *)pD;
- cout << pA << endl;
- cout << pB << endl;
- cout << pD << endl;
- cout << (pD == pB) << endl;
- return 0;
- }
这段代码的输出结果是:
可以看出,指向同一个堆上new出来的内存指针,在经过类型转换之后,其值会发生改变。究其原因,要从C++中多重继承的内存布局说起。
同时我们注意到,pB与pD的指针差值正好是ca占用的内存大小32字节,而pA与pD都指向了同一段地址。这是因为,将一个派生类的指针转换成某一个基类指针,编译器会将指针的值偏移到该基类在对象内存中的起始位置。
此时,pA 与 pD都指向ca对象的起始地址,pB 指向其起始地址刚好与ca对象的起始地址差32,所以可以看到pB打印出的地址比pA和pD的地址差32,而pA与pD均指向首地址。
接下来输出1表示pB和pD地址是相同的,其实这也是编译器屏蔽了这种指针的差异,当编译器发现一个指向派生类的指针和指向其某个基类的指针进行==运算时,会自动将指针做隐式类型提升已屏蔽多重继承带来的指针差异。因为两个指针做比较,目的通常是判断两个指针是否指向了同一个内存对象实例,在上面的场景中,pD和pB虽然指针值不等,但是他们确确实实都指向了同一个内存对象(即new cd;产生的内存对象 ),所以编译器又在此处插了一脚,让我们可以安享==运算的上层语义。
C++在多重继承下的指针类型强制类型转换的一些问题