代码走起:
class CBase
{
int nBase{ 0 };
virtual void func(){ cout << "nBase" << nBase << endl; };
};
class CA : public CBase
{
int nA;
//void func(){ cout << "nA" << nA << endl; };
};
int main()
{
cout << "size of CBase " << sizeof(CBase) << endl;
cout << "size of CA " << sizeof(CA) << endl;
return 0;
}
内存分布如下:
class CBase size(8):
+---
0 | {vfptr}
4 | nBase
+---
CBase::$vftable@:
| &CBase_meta
| 0
0 | &CBase::func
class CA size(12):
+---
| +--- (base class CBase)
0 | | {vfptr}
4 | | nBase
| +---
8 | nA
+---
CA::$vftable@:
| &CA_meta
| 0
0 | &CBase::func
CBase内有一个虚表指针,vfptr,指向对应虚表vftable,表中左侧0 表示虚函数的序号,右侧是具体函数。
CA中也有一个虚表,函数指向的是基类的实现。
如果子类重写基类的虚函数呢:
class CBase
{
int nBase{ 0 };
virtual void func(){ cout << "nBase" << nBase << endl; };
};
class CA : public CBase
{
int nA;
void func(){ cout << "nA" << nA << endl; };
};
int main()
{
cout << "size of CBase " << sizeof(CBase) << endl;
cout << "size of CA " << sizeof(CA) << endl;
return 0;
}
观其子类内存分布,可见虚表内的函数实现指向了自己的
class CA size(12):
+---
| +--- (base class CBase)
0 | | {vfptr}
4 | | nBase
| +---
8 | nA
+---
CA::$vftable@:
| &CA_meta
| 0
0 | &CA::func
编译器是在构造函数创建这个虚表指针及虚表的。
故如果调用CBase *ptr = new CA();生成的是子类的对象,在构造时,子类的对象的虚表指针指向的是的子类的虚表,ptr->func,实际上是ptr->vfptr->func,调用的是子类的虚函数,这就是多态了。
class CBase
{
public:
int nBase{ 0 };
virtual void func(){ cout << "nBase" << nBase << endl; };
};
class CA : public CBase
{
public:
int nA{1};
void func(){ cout << "nA" << nA << endl; };
};
int main()
{
CBase *ptr = new CA();
ptr->func();
ptr->CBase::func();
return 0;
}
输出:
nA1
nBase0
另注:c++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。但最好子类虚函数加上virtual表示,便于编写更上层的子类时理解。