多态是C++的三大特性之一
多态分为 动态多态,静态多态;其中静态多态分为,函数重载,泛型编程;动态多态为虚函数。
静态多态是在编译时就确定的,动态多态是在程序运行过程中确定。
本篇文章主要讨论虚函数:虚函数通过基类的引用或者基类指针来调用。
class Base { public: virtual void Funtest1(void) { cout<<"Base::Funtest1()"<<endl; } virtual void Funtest2() { cout<<"Base::Funtest2()"<<endl; } int _b; }; sizeof(Base); //结果为8
这个结果说明在base这个类中多出了四个字节那么这四个字节是干什么的?
由此可以得到多出来的四个字节是指向虚函数的地址表的简称虚表
多重继承中对象的模型
class Base1 { public: virtual void Funtest1() { cout<<"Base1::Funtest1()"<<endl; } virtual void Funtest2() { cout<<"Base1::Funtest2()"<<endl; } int _b1; }; class Base2 { public: virtual void Funtest3() { cout<<"Base2::Funtest3"<<endl; } virtual void Funtest4() { cout<<"Base2::Funtest4"<<endl; } int _b2; }; class Devire:public Base1,public Base2 { public: void Funtest2() { cout<<"Devire::Funtest2()"<<endl; } void Funtest4() { cout<<"Devire::Funtest2()"<<endl; } int _d; };
void test() { Devire d; d._b1 = 1; d._b2 = 2; d._d = 3; }
由此可见在多继承关系中派生类的对象模型中按照继承关系的声明顺序在内部对对象进行排序最后是派生类自己的成员。
其中继承自Base1中的虚函数用Base1的虚表,继承自Base2中的虚函数用Base2的虚表
重写过的虚函数有新生成的虚函数地址,没有重写的虚函数从原来的虚表中拷贝一份地址放入虚表。
菱形继承
class Base { public: virtual void Funtest() { cout<<"Base::Funtest()"<<endl; } int _b; }; class Base1:public Base { public: virtual void Funtest1() { cout<<"Base1::Funtest1()"<<endl; } virtual void Funtest2() { cout<<"Base1::Funtest2()"<<endl; } int _b1; }; class Base2:public Base { public: virtual void Funtest3() { cout<<"Base2::Funtest3"<<endl; } virtual void Funtest4() { cout<<"Base2::Funtest4"<<endl; } int _b2; }; class Devire:public Base1,public Base2 { public: void Funtest2() { cout<<"Devire::Funtest2()"<<endl; } void Funtest4() { cout<<"Devire::Funtest2()"<<endl; } int _d; }; void test1(Base1& b) { b.Funtest1(); b.Funtest2(); } void test() { Devire d; d.Base1::_b = 4; d.Base2::_b = 5 d._b1 = 1; d._b2 = 2; d._d = 3; }
对于菱形继承在派生类中的对象模型与多继承相差不大也是按照声明顺序来排列派生类自己的成员在最后,但是有了数据冗余浪费了空间。 解决数据冗余问题引入虚继承
class Base { public: virtual void Funtest() { cout<<"Base::Funtest()"<<endl; } int _b; }; class Base1:virtual public Base { public: virtual void Funtest1() { cout<<"Base1::Funtest1()"<<endl; } virtual void Funtest2() { cout<<"Base1::Funtest2()"<<endl; } int _b1; }; class Base2: virtual public Base { public: virtual void Funtest3() { cout<<"Base2::Funtest3"<<endl; } virtual void Funtest4() { cout<<"Base2::Funtest4"<<endl; } int _b2; }; class Devire: public Base1,public Base2 { public: void Funtest2() { cout<<"Devire::Funtest2()"<<endl; } void Funtest4() { cout<<"Devire::Funtest2()"<<endl; } int _d; }; void test1(Base1& b) { b.Funtest1(); b.Funtest2(); } void test() { Devire d; d._b = 4; d._b1 = 1; d._b2 = 2; d._d = 3; }
通过测试得到结果图中绿色矩形框是偏移量表对其解引用的到的第一个值是虚表相对于对象的起始位置偏移0xfffffffc是-4的补码第二个是对象起始位置相对于_b的偏移