2. 多态的定义及实现( 二 )


所以如果不实现多态,不要把函数定义成虚函数 。
4. 多态的原理 虚函数表
class Base{public:virtual void Func1(){cout << "Func1()" << endl;}private:int _b = 1;};int main(){cout << sizeof(Base) << endl;return 0;}//64位下是16//32位下是8
提问: 是不是虚函数数量越多,这个值就越大呢?(很显然不是),这个值来源究竟是什么呢?
我们通过下面这个代码来观察:
class Base{public:virtual void Func1(){cout << "Func1()" << endl;}virtual void Func2(){cout << "Base::Func2()" << endl;}virtual void Func3(){cout << "Base::Func3()" << endl;}private:int _b = 1;};class Derive : public Base{public:virtual void Func1(){cout << "Derive::Func1()" << endl;}private:int _d = 2;};int main(){Base b;Derive d;cout << sizeof(b)<<""<
1. 派生类对象d中也有一个虚表指针,d对象由两部分构成,一部分是父类继承下来的成员,虚表指针也就
是存在部分的另一部分是自己的成员 。
2. 基类b对象和派生类d对象虚表是不一样的,这里我们发现Func1完成了重写,所以d的虚表中存的是重
写的::Func1,所以虚函数的重写也叫作覆盖,覆盖就是指虚表中虚函数的覆盖 。重写是语法的
叫法,覆盖是原理层的叫法 。
3. 另外Func2继承下来后是虚函数,所以放进了虚表,Func3也继承下来了,但是不是虚函数,所以不会
放进虚表 。
4. 虚函数表本质是一个存虚函数指针的指针数组,一般情况这个数组最后面放了一个 。
5. 总结一下派生类的虚表生成:a.先将基类中的虚表内容拷贝一份到派生类虚表中 b.如果派生类重写了基
类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数 c.派生类自己新增加的虚函数按其在
派生类中的声明次序增加到派生类虚表的最后 。
6. 这里还有一个童鞋们很容易混淆的问题:虚函数存在哪的?虚表存在哪的? 答:虚函数存在虚表,虚表
存在对象中 。注意上面的回答的错的 。但是很多童鞋都是这样深以为然的 。注意虚表存的是虚函数指
针,不是虚函数,虚函数和普通函数一样的,都是存在代码段的,只是他的指针又存到了虚表中 。另外
对象中存的不是虚表,存的是虚表指针 。
多态的原理
通过下面这段代码我们再来简单分析一下:
class Student : public Person {public:virtual void BuyTicket(){cout << "买票-半价" << endl;}virtual ~Student(){cout << " ~Student()" << endl;}};void Func(Person& p){p.BuyTicket();}int main(){Person ps;Student st;Func(ps);Func(st);}
1.观察下图的红色箭头我们看到,p是 ps 对象的引用时,p-> 在ps 的虚表中找到虚函数是
::。
2.观察下图的蓝色箭头我们看到,p 是st 对象的引用时,p-> 在st 的虚表中找到虚函数
是 ::。
3.这样就实现出了不同对象去完成同一行为时,展现出不同的形态 。
4.反过来思考我们要达到多态,有两个条件,一个是虚函数覆盖,一个是对象的指针或引用调用虚函数 。反思一下为什么?
虚函数覆盖是提供了重写的方式,指针或者引用是为了寻址 。
动态绑定与静态绑定

2. 多态的定义及实现

文章插图
1. 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态,比如:函数重载
2. 动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行
为,调用具体的函数,也称为动态多态 。