关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。
隐藏是指派生类的函数屏蔽了与其同名的基类函数,有2种情况:
Item_base *baseP = &derived; double c = baseP->net_price(42); // 调用派生类中的版本 double d = baseP->Item_base::net_price(42); // 调用基类中的版本
class Base { public: virtual void f() { cout << "Base::f" << endl; } virtual void g() { cout << "Base::g" << endl; } virtual void h() { cout << "Base::h" << endl; } }; typedef void(*Fun)(void); int main(int argc, char** argv) { Base b; long* pvt = (long*) (&b); // 虚函数表地址 long* pfun1 = (long*)(*pvt); // 第一个虚函数 for (int i = 0; i < 3; i++) { Fun fun = (Fun)(*(pfun1 + i)); fun(); } return 0; }
运行结果:
Base::f
Base::g
Base::h
对象内存布局:
在上面这个图中,我在虚函数表的最后多加了一个结点,这是虚函数表的结束结点,就像字符串的结束符“/0”一样,其标志了虚函数表的结束。这个结束标志的值在不同的编译器下是不同的。
class Base { public: virtual void f() { cout << "Base::f" << endl; } virtual void g() { cout << "Base::g" << endl; } virtual void h() { cout << "Base::h" << endl; } }; class Derived: Base { public: virtual void f1() { cout << "Derived::f1" << endl; } virtual void g1() { cout << "Derived::g1" << endl; } void h1() { cout << "Derived::h1" << endl; } }; typedef void(*Fun)(void); int main(int argc, char** argv) { Base b; Derived d; long* pvt = (long*) (&d); // 虚函数表地址 long* pfun1 = (long*)(*pvt); // 第一个虚函数 for (int i = 0; i < 5; i++) { Fun fun = (Fun)(*(pfun1 + i)); fun(); } return 0; }
运行结果:
Base::f
Base::g
Base::h
Derived::f1
Derived::g1
上面的派生类没有覆盖,再看下有覆盖的场景:
class Base { public: virtual void f() { cout << "Base::f" << endl; } virtual void g() { cout << "Base::g" << endl; } virtual void h() { cout << "Base::h" << endl; } }; class Derived: Base { public: virtual void f1() { cout << "Derived::f1" << endl; } virtual void g() { cout << "Derived::g" << endl; } void h1() { cout << "Derived::h1" << endl; } }; typedef void(*Fun)(void); int main(int argc, char** argv) { Base b; Derived d; long* pvt = (long*) (&d); // 虚函数表地址 long* pfun1 = (long*)(*pvt); // 第一个虚函数 for (int i = 0; i < 4; i++) { Fun fun = (Fun)(*(pfun1 + i)); fun(); } return 0; }
运行结果:
Base::f
Derived::g
Base::h
Derived::f1
可见,派生类的虚函数g()覆盖了原本基类g()函数的位置,此时对下面的代码
Base *pb = (Base*)&d;
pb->g();
输出结果为
Derived::g
由pb所指的内存中的虚函数表的g()的位置已经被Derived::g()函数地址所取代,于是在实际调用发生时,是Derived::g()被调用了,这就实现了多态。
class Base1 { public: virtual void f() { cout << "Base1::f" << endl; } virtual void g() { cout << "Base1::g" << endl; } virtual void h() { cout << "Base1::h" << endl; } }; class Base2 { public: virtual void f() { cout << "Base2::f" << endl; } virtual void g() { cout << "Base2::g" << endl; } virtual void h() { cout << "Base2::h" << endl; } }; class Base3 { public: virtual void f() { cout << "Base3::f" << endl; } virtual void g() { cout << "Base3::g" << endl; } virtual void h() { cout << "Base3::h" << endl; } }; class Derived: Base1, Base2, Base3 { public: virtual void f1() { cout << "Derived::f1" << endl; } virtual void g1() { cout << "Derived::g1" << endl; } }; typedef void(*Fun)(void); int main(int argc, char** argv) { Derived d; long* pvt = (long*) (&d); // 虚函数表地址 long* pfun1 = (long*)(*pvt); // Base1虚函数表 long* pfun2 = (long*)(*(pvt+1)); // Base2虚函数表 long* pfun3 = (long*)(*(pvt+2)); // Base3虚函数表 cout<<"Base1 vt:"<<endl; for (int i = 0; i < 5; i++) { Fun fun = (Fun)(*(pfun1 + i)); fun(); } cout<<endl<<"Base2 vt:"<<endl; for (int i = 0; i < 3; i++) { Fun fun = (Fun)(*(pfun2 + i)); fun(); } cout<<endl<<"Base3 vt:"<<endl; for (int i = 0; i < 3; i++) { Fun fun = (Fun)(*(pfun3 + i)); fun(); } return 0; }
运行结果
Base1 vt:
Base1::f
Base1::g
Base1::h
Derived::f1
Derived::g1
Base2 vt:
Base2::f
Base2::g
Base2::h
Base3 vt:
Base3::f
Base3::g
Base3::h
对象内存布局
class Derived: Base1, Base2, Base3 { public: virtual void f() { cout << "Derived::f" << endl; } virtual void g1() { cout << "Derived::g1" << endl; } }; typedef void(*Fun)(void); int main(int argc, char** argv) { Derived d; long* pvt = (long*) (&d); // 虚函数表地址 long* pfun1 = (long*)(*pvt); // Base1虚函数表 long* pfun2 = (long*)(*(pvt+1)); // Base2虚函数表 long* pfun3 = (long*)(*(pvt+2)); // Base3虚函数表 cout<<"Base1 vt:"<<endl; for (int i = 0; i < 4; i++) { Fun fun = (Fun)(*(pfun1 + i)); fun(); } cout<<endl<<"Base2 vt:"<<endl; for (int i = 0; i < 3; i++) { Fun fun = (Fun)(*(pfun2 + i)); fun(); } cout<<endl<<"Base3 vt:"<<endl; for (int i = 0; i < 3; i++) { Fun fun = (Fun)(*(pfun3 + i)); fun(); } return 0; }
运行结果
Base1 vt:
Derived::f
Base1::g
Base1::h
Derived::g1
Base2 vt:
Derived::f
Base2::g
Base2::h
Base3 vt:
Derived::f
Base3::g
Base3::h
对象内存布局
Base1* pb1 = (Base1*)&d; pb1->f(); // Derived::f pb1->g(); // Base1::g Base2* pb2 = (Base2*)&d; pb2->f(); // Derived::f pb2->g(); // Base2::g Base3* pb3 = (Base3*)&d; pb3->f(); // Derived::f pb3->g(); // Base3::g
最后,我们知道,子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重载的。虽然在上面的图中我们可以看到Base1的虚表中有Derive的虚函数g1(),但我们根本不可能使用下面的语句来调用子类的自有虚函数:
Base1* pb1 = (Base1*)&d; pb1->g1(); // Error!
任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法,所以,这样的程序根本无法编译通过。但在运行时,我们可以通过指针的方式访问虚函数表来达到违反C++语义的行为(如上面代码例子)。
另外,如果父类的虚函数是private或是protected的,但这些非public的虚函数同样会存在于虚函数表中,所以,我们同样可以使用访问虚函数表的方式来访问这些non-public的虚函数,这是很容易做到的。
原文:https://www.cnblogs.com/chenny7/p/8523533.html