多重继承下的类型转换

主要解释强制类型转换的影响。因为static_cast会在编译期间检测,dynamice_cast会在运行时检测。
#include <iostream>
#include <hash_map>
using namespace std;
class I1
{
public:
	virtual void vf1()
	{
		cout << "I‘m I1:vf1()" << endl;
	}
};
class I2
{
public:
	virtual void vf2()
	{
		cout << "I‘m I2:vf2()" << endl;
	}
};

class C : public I1, public I2
{
private:
	hash_map<string, string> m_cache;
};

I1* CreateC()
{
	return new C();
}
int main(int argc, char** argv)
{
	I1* pI1 = CreateC();
	pI1->vf1();
	I2* pI2 = (I2*)pI1;
	pI2->vf2();
	delete pI1;
	return 0;
}
上述代码执行结果如下:

先来看看前面代码的内存布局。

之所以会出现pI1和pI2指向了同一个地址,是因为C++编译器没有足够的知识来把IA*类型转换为IB*类型,只能按照传统的C指针强制转换处理,也就是指针位置不变。为了验证上面的结论,简单的把pIA和pIB打印出来即可。把main()函数修改为如下:

int main(int argc, char** argv)

{

I1* pI1 = CreateC();

pI1->vf1();

I2* pI2 = (I2*)pI1;

pI2->vf2();

cout << "pI1指向的地址为:"<<std::hex << pI1 << endl;

cout << "pI2指向的地址为:"<<std::hex << pI2 << endl;

delete pI1;

return 0;

}

执行结果为:

可见pI1和pI2确实指向了同一个地址,而这个地址就是I1类的虚表。由于虚函数是按照顺序定位的,编译器编译pI2->vf2()的时候,不管实际的pI2指向哪里,都把它当做指向了I2的虚表,根据I2类定义,推出I2::vf2()这个函数位于其虚表的第0个位置,所以就直接把pI2指向的地址作为vf2来调用。而实际上,这个位置恰恰是I1虚表的第0个位置,也就是I1::vf1的位置,所以实际执行时调用的是I1::vf1()。其实这种情况是有些特殊的,也就是这个位置正好也是一个函数地址,而且函数原型也一样,要是有任何不同的地方,就会造成调用失败,反而更容易及时的提醒开发者。

强制类型转换

static_cast:进行编译期类型转换,此时如果C++编译期不能推算出指针调整算法,就会报错,提醒开发者。

dynamic_cast:使用dynamic_cast进行运行期动态类型转换,这需要开启编译器的RTTI。

reinterpret_cast:地址的转换,不需要检测类型。

在C++中,指针的类型转换是经常发生的事情,比如将派生类指针转换为基类指针,将基类指针转换为派生类指针。指针的本质其实就是一个整数,用以记录进程虚拟内存空间中的地址编号,而指针的类型决定了编译器对其指向的内存空间的解释方式。C++中对指针进行类型转换,不会改变指针的值,只会改变指针的类型(即改变编译器对该指针指向内存的解释方式),但是这个结论在C++多重继承下是 不成立的

#include <iostream>
using namespace std;
class CBaseA
{
public:
char m_A[32];
};
class CBaseB
{
public:
char m_B[64];
};
class CDerive : public CBaseA, public CBaseB
{
public:
char m_D[128];
};
int main()
{
auto pD = new CDerive;
auto pA = (CBaseA *)pD;
auto pB = (CBaseB *)pD;
cout << pA << ‘\n‘ << pB << ‘\n‘ << pD << endl;
cout << (pD == pB) << endl;
}

这段代码的输出是:

0x9f1080
0x9f10a0
0x9f1080
1

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

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

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

参考地址:http://blog.csdn.net/smstong/article/details/24455371

时间: 2024-10-27 03:30:44

多重继承下的类型转换的相关文章

多重继承下 Virtual Function 的语意

在多重继承中支持 virtual function, 其复杂度围绕在第二个及后继的 base classes 上, 以及必须在执行期调整 this 指针这一点, 以以下的 class 体系为例: class Base1 { public: Base1(); virtual ~Base1(); virtual void SpeakClearly(); virtual Base1* Clone() const; protected: float data_base1; }; class Base2

C++单一、多重继承下的内存模型

一:C++单一继承下的内存模型: a).最简单的一种单一继承内存模型:基类及派生类中无virtual function member: #include <iostream> class Base { public: Base(char _x = '\0') :m_x(_x) {} ~Base() {} private: char m_x; }; class Derived :public Base { public: Derived(char _x = '\0',int _y = 0,int

unicode下各种类型转换CString、string

把最近用到的各种unicode下类型转换总结了一下: 1.string转CString string a=”abc”; CString str=CString(a.c_str()); 或str.format("%s", a.c_str()) 2.int转CString Int a; CString Cstr; Cstr.Format(_T("%d"),a); 3.char 转 CString CString.format("%s", char*)

unicode下各种类型转换,CString,string,char*,int,char[]

把最近用到的各种unicode下类型转换总结了一下,今后遇到其他的再补充: 1.string转CString string a=”abc”; CString str=CString(a.c_str()); 或str.format("%s", a.c_str()) 2.int转CString Int a; CString Cstr; Cstr.Format(_T("%d"),a); 3.char 转 CString CString.format("%s&qu

C++多重继承下一个子类和父类指针转换的bug

这两天有个C++新手问了我一个问题,他的工程当中有一段代码执行不正确,不知道是什么原因.我调了一下,代码如果精简下来,大概是下面这个样子: class IBaseA { public: virtual void fnA() = 0; int m_nTestA; }; class IBaseB { public: virtual void fnB() = 0; int m_nTestB; }; class CTest : public IBaseA,public IBaseB { public:

Unicode环境下的类型转换

1.string转CString string a=”abc”; CString str=CString(a.c_str()); 或str.format("%s", a.c_str()) 2.int转CString Int a; CString Cstr; Cstr.Format(_T("%d"),a); 3.char 转 CString CString.format("%s", char*); 例: char  szPath[]; CStrin

C++ 新标准下的强制类型转换详解

使用标准C++的类型转换符:static_cast.dynamic_cast.reinterpret_cast和const_cast. static_cast 用法:static_cast<type_id> (expression) 该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性.它主要有如下几种用法: 用于类层次结构中基类和派生类之间指针或引用的转换 进行上行转换(把派生类的指针或引用转换成基类表示)是安全的进行下行转换(把基类的指针或引用转换

C++中的向上类型转换和向下类型转换+四种强制类型转换

转自博客:http://blog.csdn.net/wangweitingaabbcc/article/details/7720979# 在c++的世界中有这样两个概念,向上类型转换,向下类型转换,分别描述的是子类向基类,和基类向子类的强制类型转换. 向上强制类型转换 切割:覆盖方法和子类数据丢失的现象生成切割(slice) class Base { public: int b; virtual void Test() { cout << "base" <<en

C++中的向上类型转换和向下类型转换

在c++的世界中有这样两个概念,向上类型转换,向下类型转换,分别描述的是子类向基类,和基类向子类的强制类型转换. 向上强制类型转换 切割:覆盖方法和子类数据丢失的现象生成切割(slice) [cpp] view plain copy class Base { public: int b; virtual void Test() { cout << "base" <<endl; } }; class Derived:public Base { public: in