在派生类的对象中,基类是作为派生类的子对象存在的,称为基类子对象,当派生类只继承于一个基类时,基类子对象的起始地址是与派生类对象相同的,而当派生类同时继承于多个基类时(多重继承,这里暂不考虑虚拟继承),第一个基类子对象的起始地址是与派生类对象重合的,而后续基类子对象的起始地址与派生类对象依次相差前面的基类子对象的长度,比如,D同时派生于A、B、C,D对象的起始地址是0,那么A子对象的起始地址也是0,B子对象的起始地址是0+sizeof(A),而C子对象的起始地址为0+sizeof(A)+sizeof(B)。在下面的例子中,我们将演示this指针是如何调整的,假设C继承与A、B:
#include <iostream>
#include <stdio.h>
using namespace std;
class A{
public:
int a;
A(int i=1) : a(i) {}
void funA() { printf("A: this = %p\n",this); }
};
class B{
public:
int b;
B(int i=2) : b(i) {}
void funB() { printf("B: this = %p\n",this); }
};
class C : public A, public B {
public:
int c;
C(int i = 3) : c(i) {}
//void funB() {printf("C: this = %p\n",this); } //#1
};
int main(){
C cc;
printf("addr of cc = %p\n",&cc);
cc.funA();
cc.funB();
return 0;
}
A、B中各有一个成员变量和成员函数,当C中没有override基类的任何函数时,我们输出了在基类函数中看到的this的值,尽管这些基类函数都是由C对象调用的,输出结果如下:
addr of cc = 0xbfd0a304
A: this = 0xbfd0a304
B: this = 0xbfd0a308
可以看到,C对象的起始地址与A中看到的this是相符的,而与B中看到的相差4,这个4正好是A子对象的大小,所以编译器背后对this的值做了一些调整,如果我们把程序中的#1的注释删掉,也就是在C中override funB(),输出结果就不一样了:
addr of cc = 0xbfbfe824
A: this = 0xbfbfe824
C: this = 0xbfbfe824
可以看到,此时三个值都相同了,因为此时需要将funB()中的this指针指向C对象的起始地址,这样才可以访问C::a。