一、虚函数表指针和虚函数表
- 虚函数表指针个数与派生类继承父类个数有关,多一个父类,派生类就多一个虚函数表指针和虚函数表;
- 虚函数表是编译器为每个拥有虚函数的类创建的一个全局资源,它不属于类也不属于类对象,但它服务于类及其对象。
- 虚函数指针(vptr)是每个类对象的一部分,随着对象的创建而存在,并指向对应的虚函数表。
- 派生类和父类同时含有虚函数时,派生类的虚函数按照父类声明的顺序(从左到右),存放在第一个父类的虚函数表后面;
- 成员变量按照先声明、先存储、先父类、再子类的顺序存放;
- 无论是派生类还是父类,当出现虚函数(普通虚函数、纯虚函数、虚虚构函数),虚函数表指针放在内存的最前面;
二、覆盖和继承
- 派生类和父类出现同名成员变量时,派生类仅仅时将父类的同名承运隐藏了,而非覆盖替换;
- 派生类调用成员变量时,按就近原则,调用自身的同名变量,避免了调用变量时的二义性;
- 派生类出现和父类同名的虚函数时,派生类的虚函数表中将子类同名的函数地址替换为自身的同名虚函数地址,即多态;
三、实例
1、多继承,派生类有虚函数
// 基类
class baseA
{
public:
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
int _mc = 3;
int _md = 4;
};
// 派生类
class deriveA : public baseA, public baseB
{
public:
virtual void print() { std::cout << "deriveA::print()\n\n\n"; }
int _me = 3;
int _mf = 4;
};
内存分布
1>class deriveA size(28):
1> +---
1> 0 | {vfptr}
1> 4 | +--- (base class baseA)
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | _mc
1>16 | | _md
1> | +---
1>20 | _me
1>24 | _mf
1> +---
1>
1>deriveA::$vftable@:
1> | &deriveA_meta
1> | 0
1> 0 | &deriveA::print
2、多继承、派生类重载基类虚函数
// 基类
class baseA
{
public:
virtual void show() { std::cout << "virtual baseA::show() \n\n"; }
int _ma = 1;
int _mb = 2;
};
class baseB
{
public:
virtual void play() { std::cout << "virtual baseB::play() \n\n"; }
int _mc = 3;
int _md = 4;
};
class baseC
{
public:
virtual void listening() { std::cout << "virtual baseC::listening() \n\n"; }
int _mh = 7;
int _mi = 8;
};
// 派生类
class deriveA : public baseA, public baseB, public baseC
{
public:
virtual void show() { std::cout << "virtual deriveA::show() \n\n"; }//派生类即使不是虚函数,内存分布也一样
int _me = 3;
int _mf = 4;
};
内存分布
1>class deriveA size(44):
1> +---
1> 0 | +--- (base class baseA)
1> 0 | | {vfptr}
1> 4 | | _ma
1> 8 | | _mb
1> | +---
1>12 | +--- (base class baseB)
1>12 | | {vfptr}
1>16 | | _mc
1>20 | | _md
1> | +---
1>24 | +--- (base class baseC)
1>24 | | {vfptr}
1>28 | | _mh
1>32 | | _mi
1> | +---
1>36 | _me
1>40 | _mf
1> +---
1>
1>deriveA::$vftable@baseA@:
1> | &deriveA_meta
1> | 0
1> 0 | &deriveA::show
1>
1>deriveA::$vftable@baseB@:
1> | -12
1> 0 | &baseB::play
1>
1>deriveA::$vftable@baseC@:
1> | -24
1> 0 | &baseC::listening
1>
1>deriveA::show this adjustor: 0