我知道在派生类中我们可以使用基类指针来寻址和调用虚拟函数,因为基类指针的指针范围比较有限。但我只想知道,基类指针如何知道在派生类中从哪里开始?
例如,为了记录A、B和C都有自己的数据成员,我们可以把这个问题分成两类来讨论 1.A、B和C都有自己的数据成员。A, B & C都有自己的虚拟函数; 2. A, B & C只有自己的数据成员,没有任何虚拟函数。
class A {...};
class B {...};
class C : public A, public B {...};
C c;
B* b = &c
在C里面,A应该放在B的上面,那么指针b怎么知道在C里面从哪里开始寻址呢?
编译器知道布局,所以会发出代码,会计算出地址的 B
中的子对象 C
.
我们来看一个简单的实际例子。
class A { int x; };
class B { int y; };
class C: public A, public B { };
B *get(C &c) {
return &c;
}
在 get
,编译器必须计算出 B
居于 C
. 下面是一个编译代码的例子(榫头):
get(C&): # @get(C&)
lea rax, [rdi + 4]
ret
这意味着编译器将在输入参数的地址上增加4个字节(c
),并返回(它增加了4,因为 sizeof(A)
是4,而编译器决定不添加任何额外的填充)。)
注意,如果 A
是空的,那么很可能是地址为 B
同为 C
. 如果 A
不是空的(有非静态数据成员,或有虚拟函数),那么 B
的地址可能会有所不同。但这些都是实现细节,取决于平台的ABI(Application Binary Interface)。