考虑以下计划
#include<iostream>
using namespace std;
class ClassA
{
public:
virtual ~ClassA(){};
virtual void FunctionA(){};
};
class ClassB
{
public:
virtual void FunctionB(){};
};
class ClassC : public ClassA,public ClassB
{
};
void main()
{
ClassC aObject;
ClassA* pA = &aObject;
ClassB* pB = &aObject;
ClassC* pC = &aObject;
cout<<"pA = "<<pA<<endl;
cout<<"pB = "<<pB<<endl;
cout<<"pC = "<<pC<<endl;
}
pA,pB,pC应该相等,但结果是
pA = 0031FD90
pB = 0031FD94
pC = 0031FD90
为什么pB = pA + 4? 当我改变
class ClassA
{
public:
virtual ~ClassA(){};
virtual void FunctionA(){};
};
class ClassB
{
public:
virtual void FunctionB(){};
};
到
class ClassA
{
};
class ClassB
{
};
结果是
pA = 0030FAA3
pB = 0030FAA4
pC = 0030FAA3
pB = pA + 1?
答案 0 :(得分:1)
乘法继承的对象有两个合并的子对象。我会猜测编译器指向内部对象的指针之一。
答案 1 :(得分:0)
C有两个继承的子对象,因此是A对象和B对象的串联。当你有一个对象C时,它由一个对象A后跟一个对象B组成。它们不在同一个地址,这就是原因。所有三个指针都指向同一个对象,但是指向不同的超类。编译器会为您做出改变,因此您不必担心这一点。
现在。为什么一个案例中有4个而另一个案例中有1个不同?好吧,在第一种情况下,你有A和B的虚函数,因此每个子对象必须有一个指向其vtable的指针(该表包含已解析的虚函数调用的地址)。所以在这种情况下,sizeof(A)
是4.在第二种情况下,你没有虚函数,所以没有vtable。但是每个子对象必须是独立可寻址的,因此编译器仍然必须为类A的子对象和类B的子对象分配不同的地址。两个地址之间的最小差异是1.但我想知道EBO(空基类)优化)在这种情况下不应该踢。
答案 2 :(得分:0)
这是编译器的实现细节。
您遇到此案例的原因是您的代码中有MI
。
考虑计算机如何访问ClassB
中的成员,它使用偏移来访问成员。因此,假设您在B类中有两个int,它使用以下语句来访问第二个int成员。
*((int*)pb + 1) // this actually will be assembly generate by compiler
但如果pb
指向类中aObject
的开头,这将不再起作用,因此编译器需要生成多个程序集版本才能访问该类的同一成员库继承结构,并有运行时成本。
这就是为什么编译器调整pb不等于pa,这将使上面的代码工作,这是最简单有效的实现方式。
这也解释了为什么pa == pc
但不等于pb
。