1、引言
本篇介绍C++中的对象模型中虚指针vptr,虚表vtbl,动态绑定Dynamic Binding的相关知识点。
2、关于对象模型虚指针和虚表
如果C++类成员中有虚函数,那么对应的对象中就有除了本身数据外的虚指针vptr(4个字节),虚指针指向虚表vtbl,虚表中存放虚函数的地址。如下图所示:
注意点:如图当指针p指向对象c,用p调用c中的虚函数vfunc1(),即p->func1()时的过程是:
a、首先,p会先指向虚指针 p->vptr;
b、接着,通过虚指针对象虚表找到调用函数的地址(*(p->vptr)[n]);
c、最后,通过对象调用实现(*(p->vptr)[n])(p)。
3、关于对象模型中的this
如下图,CDocument类和CMyDoc类为继承关系,serialize()为成员函数CDocument::OnFileOpen()中的一个虚函数:
当使用主程序:
CMyDoc myDoc;
myDoc.OnFileOpen(); //相当于CDocument::OnFileOpen(this);或相当于CDocument::OnFileOpen(&myDoc)
注意点:
a、如注释所示,当子类调用父类方法时,在调用方法中有个隐藏的this,代表子类对象;
b、当执行OnFileOpen()函数时,执行到Serialize()函数,其实是执行this->Serialize(),而this代表子类,所以会调用子类的虚函数Serialize()。
4、关于对象模型中的动态绑定
1)示例
class Animal
{
public:
Animal(){ }
virtual void eat() const {
cout << "animal" << endl;
}
private:
int age;
};
class Horse:public Animal
{
public:
Horse(){ }
virtual void eat() const {
cout << "horse" << endl;
}
private:
int num;
};
说明:头文件中设计用于测试,两个类是继承关系,有一个虚函数eat().
2)主程序
int _tmain(int argc, _TCHAR* argv[])
{
Horse h;
h.eat(); //#1输出:horse
Animal a1 = h;
a1.eat(); //#2输出:animal
//【通过对象调用,是静态绑定Animal::eat()】
Animal* a = new Horse;
a->eat(); //#3输出:horse
return 0;
}
注意点:
a、#2输出的是父类的虚函数,因为这是静态绑定,通过父类对象调用虚函数;
b、#3输出的子类的虚函数,因为这是动态绑定,满足动态绑定的条件①通过up-cast方式;②通过指针调用;③调用的是虚函数。