关于C++:为什么PA、PB、PC不相等?

Why are pA,pB,pC not equal?

考虑以下程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 #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?当我改变的时候

1
2
3
4
5
6
7
8
9
10
11
12
class ClassA
{
public:
    virtual ~ClassA(){};
    virtual void FunctionA(){};

};
class ClassB
{
 public:
    virtual void FunctionB(){};
};

1
2
3
4
5
6
7
class ClassA
{
};

class ClassB
{
};

结果是

pA = 0030FAA3

pB = 0030FAA4

pC = 0030FAA3

Pb=Pa+1?


乘法继承的对象有两个合并的子对象。我猜编译器将其中一个指针指向一个内部对象。


这是编译器的实现细节。您之所以打这个案例是因为您的代码中有MI

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

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

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

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

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


C有两个继承的子对象,因此是A对象和B对象的串联。当你有一个对象C时,它由一个对象A和一个对象B组成。它们不在同一个地址,这就是为什么。所有三个指针都指向同一个对象,但作为不同的超类。编译器为您做出了改变,所以您不必为此担心。

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