第一次写自己的c++学习心得,欢迎大家拍砖
在多继承中,基类指针是如何指向派生类对象的?
各位看官看到这个题目可能要笑了,这么简单清楚的事情也要罗嗦,其实,简单的事情仔细想想,有时也能想出问题来.
好了,闲话少叙,先说一段关于多继承的程序.
#include <iostream>
#include <conio.h>
using namespace std;
//
class Base{
public:
int _i1;
int _i2;
int _i3;
virtual void displayA()
{
cout<<"in base,display function."<<endl; }
};
//
class AnotherBase{
public:
int _k;
virtual void displayB()
{
cout<<"in anotherbase,displayB function."<<endl;
}
};
//
class Derive:public Base,public AnotherBase{
public:
int _n;
void displayA() {cout<<"derive ,displayA."<<endl;}
void displayB() {cout<<"derive ,displayB."<<endl;}
};
int main()
{
Derive myDerive,*pDerive=&myDerive;
Base *pBase=NULL;
AnotherBase *pAnotherBase=NULL;
pBase=pDerive; // <--- attention
pAnotherBase=pDerive; // <--- attention
cout<<"the address stored in pBase="<<pBase<<endl;
cout<<"the address stored in pAnotherBase="<<pAnotherBase<<endl;
//cout<<"size of class Base is "<<sizeof(Base)<<endl;
//cout<<"size of class AnotherBase is "<<sizeof(AnotherBase)<<endl;
//cout<<"size of class Derive is "<<sizeof(Derive)<<endl;
getchar();
return 0;
}
每个人的运行结果都不会一样。我这里的结果是
the address stored in pBase=0012FF64
the address stored in pAnotherBase=0012FF74
是不是有一些惊讶?同一个pDerive分别赋值给pBase,pAnotherBase指针,居然产生了两个不同的值。让我们再仔细的看一下:pAnotherBase和pBase的差值为16个字节,而这16个字节恰好为一个Base类对象的大小!口说无凭,将程序最后几行中的注释去掉后再运行,就可以看到结果。
size of class Base is 16
size of class AnotherBase is 8
size of class Derive is 28
在我们做出最后的解答前,先提出两个问题:
1 基类的对象在内存中是如何存储的?
2 派生类对象在内存中是如何存储的?
我们先来看看问题1:
任何成员函数都不占据存储空间,所以对于基类对象来说,只有非静态的成员变量才占据内存空间。对于我们的Base类来说,其对象的大小为sizeof(int)*3=12,
还有4个字节呢?这4个字节就是vptr.当然,如果类中没有虚函数,也就没有vptr。 这个问题,在<<深入浅出MFC>>64-68页有过论述。
解决了第一个问题,第二个问题也好办了,派生类对象是先存储了基类子对象后,再存储派生类自己特有的部分。可是又有一个问题,在多继承中,派生类对象含有多个基类子对象,谁先?谁后?奥秘就在派生类的派生表里,哪个基类在先,哪个基类的子对象就先存储。(对于这个问题,你可以将Derive类派生表中Base和Another类的位置调换,再重新运行程序,你会发现pBase和pAnotherBase的差值变为-8,其绝对值正好是AnotherBase类对象的大小)
好,现在我们就将Derive类的对象在内存中的结构写出来。
内容 地址 字节大小
vptr(1) (Base类子对象) 0012FF64 4 字节
_i1 (Base类子对象) 0012FF68 4 字节
_i2 (Base类子对象) 0012FF6C 4 字节
_i3 (Base类子对象) 0012FF70 4 字节
vptr(2) (AnotherBase类子对象) 0012FF74 4 字节
_k (AnotherBase类子对象) 0012FF78 4 字节
_n (Derive类特有的部分) 0012FF7C 4 字节
Total 4*7=28=sizeof(Derive)
从这个结构中我们可以看出,为什么同一个pDerive分别赋值给pBase,pAnotherBase,这两个指针的值会不一样。为了在多继承中完成多态,编译器必须做一些手脚,使得pBase和pAnotherBase指向不同的地址。如果pAnother指向的不是vptr(2)处,而是vptr(1)处,呵呵,灾难将会发生。因为对于AnotherBase类来说
它不知道Base类的信息,所以它也无法解释从vptr(1)开始的内容。
得到的结论是:
在多继承中,将基类指针指向一个派生类的对象时,系统并不是简单的将对象的首地址赋值给基类指针。基类指针指向的地址为派生类中相应基类子对象的地址。而这个地址,未必就是派生类对象的首地址。