多态的原理
-
虚函数表
class base { public: virtual void func() { cout << "func" << endl; } private: int a_; }; int main() { base b; cout << sizeof(b) << endl; // 8 32位 return 0; }
发现输出是8个字节,int占4个字节,那么是什么占用了空间呢
发现除了a__外还有一个_vfptr数组, 数组中存放虚函数的指针
-
派生类中虚表覆盖
class base { public: virtual void func() { cout << "func" << endl; } virtual void func1() { cout << "func1" << endl; } void func2() { cout << "func2" << endl; } private: int a_; }; class derive : public base { public: virtual void func() override { cout << "derive func" << endl; } private: int b_; }; int main() { base b; derive d; return 0; }
可以看到派生类对象中继承自基类的_vfptr数组中被重写的虚函数已经变成派生类中的函数指针
-
结论
- 类中虚函数的指针会被存在虚表中,虚表是一个指针数组,这个数组最后面放了一个nullptr
- 不是虚函数的函数,其函数指针不会被放进虚表
- 派生类的虚表生成, 先将基类中的虚表内容拷贝一份到派生类虚表中,然后看派生类中是否重写了基类的虚函数,如果重写就将派生类中的虚函数指针覆盖基类的虚函数, 派生类新增的虚函数按其在派生类的声明次序增加到派生类虚表的最后
- 虚表存的是虚函数指针,不是虚函数,虚函数和普通函数一样都存在代码段
-
多态条件
- 虚函数覆盖
- 对象的指针或引用调用虚函数
derive d; base a = d; base* p = &d; base& r = d;
必须用指针或引用调用虚函数才能构成多态的原因是, 满足多态以后的函数调用是在运行的时候从对象里找的, 不满足多态的函数是编译时确认好的 -
动态绑定与静态绑定
静态绑定又称前期绑定,在程序编译器确定了程序的行为,也称静态多态,比如函数重载
满足多态以后的函数调用是在运行的时候从对象里找的, 不满足多态的函数是编译时确认好的
-
动态绑定与静态绑定
静态绑定又称前期绑定,在程序编译器确定了程序的行为,也称静态多态,比如函数重载
动态绑定又称后期绑定,是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数