6

考虑以下程序

 #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?

4

3 回答 3

1

The multiply inherited object has two merged sub-objects. I would guess the compiler is pointing one of the pointers to an internal object.

于 2013-10-17T02:45:52.713 回答
0

C 有两个继承的子对象,因此是 A 对象和 B 对象的串联。当你有一个对象 C 时,它由一个对象 A 和一个对象 B 组成。它们不在同一个地址,这就是原因。所有三个指针都指向同一个对象,但作为不同的超类。编译器会为您进行转换,因此您不必担心。

现在。为什么在一种情况下相差 4 而在另一种情况下相差 1?好吧,在第一种情况下,A 和 B 都有虚函数,因此每个子对象都必须有一个指向其 vtable 的指针(该表包含已解析的虚函数调用的地址)。所以在这种情况下,sizeof(A)是 4。在第二种情况下,你没有虚函数,所以没有 vtable。但是每个子对象必须是独立可寻址的,所以编译器仍然需要为A类的子对象和B类的子对象分配不同的地址。两个地址之间的最小差异是1。但是我想知道EBO(空基类优化)在这种情况下不应该启动。

于 2013-10-17T06:35:57.527 回答
0

这就是编译器的实现细节。您遇到这种情况的原因是因为您MI的代码中有。

想想计算机如何访问成员ClassB,它使用偏移量来访问成员。因此,假设您在 B 类中有两个 int,它使用以下语句访问第二个 int 成员。

  *((int*)pb + 1) // this actually will be assembly generate by compiler

但是如果pb指向aObject你的类的开头,这将不再起作用,因此编译器需要生成多个程序集版本来访问基于类继承结构的相同成员,并且具有运行时成本。

这就是编译器调整pb不等于pa的原因,这将使上面的代码工作,这是最简单有效的实现方式。

这也解释了为什么pa == pc但不等于pb.

于 2013-10-17T10:40:49.950 回答