C++虚函数原理
目录
多继承:存在虚函数覆盖同时又存在自身定义的虚函数的类对象布局
虚函数表的布局
无继承关系的虚函数
如果一个类中声明了一个虚函数,那么编译器就会为这个类在所有变量的前面增加了一个void **的指针变量__vfptr(注意不是数组),我们称__vfptr指向的void*数组为虚函数表
这个类每添加一个虚函数,void **变量__vfptr里就会增加一个void*的指针
同一个类的不同实例都有一个__vfptr指针,指向同一个虚函数表
虚函数表保存在哪里?
- 它是编译器在编译时期为我们创建好的, 只存在一份
- 定义类对象时, 编译器自动将类对象的__vfptr指向这个虚函数表
存在继承关系的虚函数
单继承:子类不覆盖基类虚函数的情况
class Base1
{
public:
int base1_1;
int base1_2;
virtual void base1_fun1() {}
virtual void base1_fun2() {}
};
class Derive1 : public Base1
{
public:
int derive1_1;
int derive1_2;
};
单继承:子类覆盖了基类的一个虚函数的情况
class Base1
{
public:
int base1_1;
int base1_2;
virtual void base1_fun1() {}
virtual void base1_fun2() {}
};
class Derive1 : public Base1
{
public:
int derive1_1;
int derive1_2;
// 覆盖基类函数
virtual void base1_fun1() {}
};
无论是通过Derive1的指针还是Base1的指针来调用此函数, 调用的都将是被继承类重写后的那个函数
单继承:定义了基类没有的虚函数
class Base1
{
public:
int base1_1;
int base1_2;
virtual void base1_fun1() {}
virtual void base1_fun2() {}
};
class Derive1 : public Base1
{
public:
int derive1_1;
int derive1_2;
virtual void derive1_fun1() {}
};
- 继承类Derive1的虚函数表被加在基类的后面
- 由于Base1只知道自己的两个虚函数索引[0][1], 所以就算在后面加上了[2], Base1根本不知情, 不会对它造成任何影响
多继承:存在虚函数覆盖同时又存在自身定义的虚函数的类对象布局
class Base1
{
public:
int base1_1;
int base1_2;
virtual void base1_fun1() {}
virtual void base1_fun2() {}
};
class Base2
{
public:
int base2_1;
int base2_2;
virtual void base2_fun1() {}
virtual void base2_fun2() {}
};
// 多继承
class Derive1 : public Base1, public Base2
{
public:
int derive1_1;
int derive1_2;
// 基类虚函数覆盖
virtual void base1_fun1() {}
virtual void base2_fun2() {}
// 自身定义的虚函数
virtual void derive1_fun1() {}
virtual void derive1_fun2() {}
};
Derive1的虚函数表依然是保存到第1个拥有虚函数表的那个基类的后面的
如果第1个直接基类没有虚函数(表)
即
class Base1
{
public:
int base1_1;
int base1_2;
};
class Base2
{
public:
int base2_1;
int base2_2;
virtual void base2_fun1() {}
virtual void base2_fun2() {}
};
// 多继承
class Derive1 : public Base1, public Base2
{
public:
int derive1_1;
int derive1_2;
// 自身定义的虚函数
virtual void derive1_fun1() {}
virtual void derive1_fun2() {}
};
那么哪个基类有虚函数表,哪个类就会被放到前面,Derive1的虚函数表仍然保存到最前面哪个拥有虚函数表的那个基类的后面
如果两个基类都没有虚函数表
即
class Base1
{
public:
int base1_1;
int base1_2;
};
class Base2
{
public:
int base2_1;
int base2_2;
};
// 多继承
class Derive1 : public Base1, public Base2
{
public:
int derive1_1;
int derive1_2;
// 自身定义的虚函数
virtual void derive1_fun1() {}
virtual void derive1_fun2() {}
};
多继承:如果有三个基类: 虚函数表分别是有, 没有, 有
那么有虚虚函数表的都会被提到前面
父子对象指针间的转换与函数调用
如果把一个子类指针转为父类指针,那么调用函数会按一下规则调用
- 如果不是虚函数, 直接调用指针对应的基本类的那个函数
- 如果是虚函数, 则查找虚函数表, 并进行后续的调用. 虚函数表在定义一个时, 编译器就为我们创建好了的. 所有的, 同一个类, 共用同一份虚函数表
以上全部整理总结自:https://blog.twofei.com/496/(感谢,无敌详细版请参照它)