C++多态
来源:互联网 发布:linux网络运维 编辑:程序博客网 时间:2024/06/05 05:36
一、C++多态
多态是指一组具有继承关系的类,拥有相同的接口(函数名、形参和返回值),并允许有各自不同的实现,且一个对象实例只有在调用这共同接口的时候,才能确定调用的是何种实现。 即,“一个接口,多种实现”
二、静态多态
编译器在编译期间完成的
静态多态 : 通过”彼此单独定义但支持共同操作的具体类“来表达共同性。
函数多态:即函数重载
概念:基于不同的参数列表,同一函数名可指向不同的函数体实现。
三、动态多态
动态绑定:在程序执行期间判断所引用对象的实际类型,根据其实际类型调用相应的方法。
以类继承和虚函数机制为基础,表达共同接口。
动态多态是为面向对象编程服务的。因此,多态通常是指动态多态。
1、使用virtual关键字:
使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定
比如下面这个很有意思的例子:
class CWashRoom{public: void GoToManWashRoom() { cout << "MAN--->Please Left" << endl; } void GoToWomanWashRoom() { cout << "WOMAN--->Please Right" << endl; }};class CPerson{public: virtual void GoToWashRoom(CWashRoom& _washRoom) = 0;//纯虚函数};class CMan :public CPerson{public: virtual void GoToWashRoom(CWashRoom& _washRoom) { _washRoom.GoToManWashRoom(); }};class CWoman :public CPerson{public: virtual void GoToWashRoom(CWashRoom& _washRoom) { _washRoom.GoToWomanWashRoom(); }};void Test(){ CWashRoom washRoom; for (int iIdx = 1; iIdx <= 10; ++iIdx) { CPerson* pPerson; int iPerson = rand() % iIdx; if (iPerson & 0x01) { pPerson = new CMan; } else { pPerson = new CWoman; } pPerson->GoToWashRoom(washRoom); delete pPerson; pPerson = NULL; Sleep(1000); }}
2、虚函数的声明:
虚函数: 虚函数必须是被关键字virtual修饰的某类的非静态成员函数。
声明方法: virtual <函数返回值类型> <函数名>(<参数表>);
注意: 在派生类中重定义的虚函数必须和基类的虚函数完全相同(协变除外),即具有相同的返回类型、参数个数和参数类型。否则就是一种语法错误。
3、继承体系中容易搞混的三种同名函数的关系:
4、虚函数的总结:
为什么静态成员函数不能定义为虚函数
为什么构造函数不能定义为虚函数,为什么operator=可以定义成虚函数,但又不建议这样做
6、分析为什么构造函数和析构函数中不能调用虚函数,以及调用之后存在的问题
7、最好将析构函数声明为虚函数,为什么?
class Base{public: Base* func() { cout << "Base::func1" << endl; return this; } virtual~Base() //~Base() { cout << "~Base()" << endl; }};class Derive :public Base{public: Derive* func() { cout << "Derive::func1" << endl; return this; } ~Derive() { cout << "~Derive()" << endl; }};int main(){ Base* ptr = new Derive; delete ptr; getchar(); return 0;}
class Base{public: Base() { cout << "Base()" << endl; } ~Base() { cout << "~Base()" << endl; }private: int _b;};class Derived :public Base{public: Derived() { cout << "Derived()" << endl; p = new int(0); } ~Derived() { cout << "Derived()" << endl; delete p; }private: int _d; int* p;};void Test(){ Base* pb = new Derived;//如果析构函数不是虚函数,那么就是普通函数的调用方法,直接调用,不会去查函数虚表,这样pb是基类的指针,它只会调用基类的析构函数 delete pb;}int main(){ Test(); getchar(); return 0;}
验证结果:
四、虚函数表vftable(virtual function table):
先来看一段代码:
class Base{public: virtual void FunTest1() { cout << "Base::FunTest1()" << endl; }private: int _b;};class Derived :public Base{public: virtual void FunTest1() { cout << "Derived::FunTest1()" << endl; }private: int _d;};int main(){ cout << sizeof(Derived) << endl; return 0;}
按照以前的算法 sizeof(D)是8,个字节,那么多出来的4个字节又是什么呢?
调用监视窗口可以看到有两个指针 _vfptr (虚表指针)
1、普通继承(单):
class B{public: virtual void FunTest1() { cout << "B::FunTest1()" << endl; } virtual void FunTest2() { cout << "B::FunTest2()" << endl; } int _b;};class C :public B{public: virtual void FunTest1() { cout << "C::FunTest1()" << endl; } virtual void FunTest3() { cout << "C::FunTest3()" << endl; } int _c;};void Test(B& b){ b.FunTest1(); b.FunTest2();}int main(){ B b; C c; Test(b); Test(c); b._b = 1; c._c = 2; cout << sizeof(C) << endl;//12 getchar(); return 0;}
2、多继承:
class B1{public: virtual void FunTest1() { cout << "B1::FunTest1()" << endl; } virtual void FunTest2() { cout << "B1::FunTest2()" << endl; } int _b1;};class B2{public: virtual void FunTest3() { cout << "B2::FunTest3()" << endl; } virtual void FunTest4() { cout << "B2::FunTest4()" << endl; } int _b2;};class C :public B1, public B2{public: virtual void FunTest1() { cout << "C::FunTest1()" << endl; } virtual void FunTest3() { cout << "C::FunTest3()" << endl; } virtual void FunTest5() { cout << "C::FunTest5()" << endl; } int _c;};int main(){ cout << sizeof(C) << endl;//20 B1 b1; B2 b2; C c; B1* p1 = &b1; p1->FunTest1(); p1->FunTest2(); cout << endl; B2* p2 = &b2; p2->FunTest3(); p2->FunTest4(); cout << endl; C* pc = &c; pc->FunTest3(); pc->FunTest5(); getchar(); return 0;}
3、虚拟继承:
class B{public: virtual void FunTest1() { cout << "B::FunTest1()" << endl; } virtual void FunTest2() { cout << "B::FunTest2()" << endl; } int b;};class C :virtual public B{public: virtual void FunTest1() { cout << "C::FunTest1()" << endl; } virtual void FunTest3() { cout << "C::FunTest3()" << endl; } int c;};int main(){ cout << sizeof(C) << endl;//20 B b; C c; b.b = 1; c.c = 2; B* p1 = &b; p1->FunTest1(); p1->FunTest2(); cout << endl; C* pc = &c; pc->FunTest1(); pc->FunTest3(); getchar(); return 0;}
4、菱形继承:
class B{public: virtual void FunTest1() { cout << "B::FunTest1()" << endl; } virtual void FunTest2() { cout << "B::FunTest2()" << endl; } int b;};class C1 :virtual public B{public: virtual void FunTest1() { cout << "C1::FunTest1()" << endl; } virtual void FunTest3() { cout << "C1::FunTest3()" << endl; } int c1;};class C2 :virtual public B{public: virtual void FunTest1() { cout << "C2::FunTest1()" << endl; } virtual void FunTest4() { cout << "C2::FunTest4()" << endl; } int c2;};class D :public C1, public C2{public: virtual void FunTest1() { cout << "D::FunTest1()" << endl; } virtual void FunTest3() { cout << "D::FunTest3()" << endl; } virtual void FunTest5() { cout << "D::FunTest5()" << endl; } int d;};int main(){ cout << sizeof(D) << endl; getchar(); return 0;}
这次为什么D的大小是36 呢?
这时我们就需要分析其对象模型
虚表指针:
每一个多态类都有且仅有一个存储所有虚函数入口地址的数组。
虚函数表指针vptr:
每一个多态类的所有对象都有且仅有一个的隐藏成员vptr,指向虚函数表vftable.
vptr和vftable的初始化:
基类对象的vptr指向基类的vftable。
派生类对象vptr指向该派生类的vftable。
如果派生类没有覆盖(override)父类的虚函数,则其vftable中对应表项指向其父类的此函数。 如果派生类覆盖了父类的虚函数,则vftable中对应表项指向重写后的此函数。 如果派生类定义了新的虚函数,则此函数的地址将被添加到vftable中
虚函数的工作原理:
多态的函数调用语句被编译成一系列根据基类指针所指向的(或基类引用所引用的)对象中存放的虚函数表的地址,在虚函数表中查找虚函数地址,并调用虚函数的过程。
vptr和vftable在内存中的存储
class Base{public: virtual void func1() { cout << "Base::func1" << endl; } virtual void func2() { cout << "Base::func2" << endl; } void func3() { cout << "Base::func3" << endl; }};class Derive :public Base{public: virtual void func1() { cout << "Derive::func1" << endl; } virtual void func3()//构成重载 func也要放在虚表中 { cout << "Derive::func3" << endl; } virtual void func4() { cout << "Derive::func3" << endl; }};typedef void(*V_FUNC)();void PrintVTable(int vtable)//打印虚表{ int* vf_array = (int*)vtable; printf("vtable:0x%p\n",vtable); for (size_t i = 0; vf_array[i] != 0; ++i) { printf("vatable[%d]:0x%p->", i, vf_array[i]); V_FUNC f = (V_FUNC)vf_array[i]; f(); } printf("----------------------------\n");}int main(){ Base b; Derive d; PrintVTable(*(int*)&b); PrintVTable(*(int*)&d); Base* ptr=&d; ptr->func1();//构成多态 ptr->func3();//不是多态 getchar(); return 0;}
- [C/C++]多态
- C++--多态
- C++-----------------多态
- C 多态
- c++-->多态
- 【C++】多态
- 【C#】 多态
- 【C#】多态
- 【c++】多态
- 多态(C++)
- 【C++】多态
- <c++>多态
- C++--多态
- Objective-c 多态
- Objective-C多态
- Objective-C多态(二)
- Objective-C 多态
- C++ 多态
- 树状数组(模板)
- Windows Practice_Dll&Hook_DetourHook库
- 基于Excel的QR二维码生成工具——原理及算法详解(之六)
- windows 环境下在python中安装tensorflow报错问题
- 集成学习
- C++多态
- 1
- 17.10.3日报
- 深入理解ES6 -- 函数
- mysql列值相加为空
- [BeiJing2006]狼抓兔子 平面图最小割
- leetcode之深搜递归回溯类-----1/167/653. two sum(记忆化搜索寻找和为给定值的两个数)
- java设计模式(一)——普通工厂模式
- <C++>10.对象的定义和使用