C++之多态
来源:互联网 发布:西安交通大学软件学院 编辑:程序博客网 时间:2024/04/19 06:22
多态
多态就是指一种事物在不同时期的不同体现。
多态有两张情况,动态多态和静态多态
静态多态
在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推断出要调用那个函数,如果有对应的函数就调用该函数,否则出现编译错误。
静态多态的实现方法有两种:
1.重载
#include <iostream>using namespace std;int add(int left, int right){ return left + right;}float add(float left, float right){ return left + right;}int main(){ cout << add(10, 20) << endl; cout << add(3.13f, 2.14f) << endl; system("pause"); return 0;}
2.泛型编程
关于泛型编程我还没有了解太多,会在以后的学习中了解并总结。
动态多态
动态绑定:在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应的方法。使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定。
#include <iostream>#include <Windows.h>using namespace std;class WashRoom{public: void GoToManWashRoom() { cout << "Man-->Please Left" << endl; } void GoToWomanWashRoom() { cout << "Woman-->Please Right" << endl; }};class Person{public: virtual void GoToWashRoom(WashRoom & washRoom) = 0;};class Man :public Person{public: virtual void GoToWashRoom(WashRoom & washRoom) { washRoom.GoToManWashRoom(); }};class Woman :public Person{public: virtual void GoToWashRoom(WashRoom & washRoom) { washRoom.GoToWomanWashRoom(); }};void FunTest(){ WashRoom washRoom; for (int iIdx = 1; iIdx <= 10; ++iIdx) { Person* pPerson; int iPerson = rand() % iIdx; if (iPerson & 0x01) pPerson = new Man; else pPerson = new Woman; pPerson->GoToWashRoom(washRoom); delete pPerson; pPerson = NULL; Sleep(1000); }}int main(){ FunTest(); system("pause"); return 0;}
但是想要实现多态绑定必须满足几点条件:
1.通过基类类型的引用或者指针调用虚函数
对象的类型
静态类型:对象声明时的类型,在编译期间确定。动态类型:目前所指对象的类型,在运行期间确定。
class CBase{};class CDe1:public CBase{};class CDe2:public CBase{};int main(){ //pd1的静态类型是CDe1*,动态类型是CDe1 CDe1* pd1 = new CDe1; //pb的静态类型是CBase*,动态类型是CDe1 CBase* pb = pd1; CDe2* pd2 = new CDe2; //pb的动态类型现在是CDe2 pb = pd2; system("pause"); return 0;}
2.必须是虚函数并且派生类一定要对虚函数进行重写
class Base{public : virtual void FunTest1( int _iTest) { cout <<"Base::FunTest1()" << endl; } void FunTest2(int _iTest) { cout <<"Base::FunTest2()" << endl; } virtual void FunTest3(int _iTest1) { cout <<"Base::FunTest3()" << endl; } virtual void FunTest4( int _iTest) { cout <<"Base::FunTest4()" << endl; }};class Derived :public Base{public : //正确 virtual void FunTest1(int _iTest) { cout <<"Derived::FunTest1()" << endl; } //错误,基类中不是虚函数, virtual void FunTest2(int _iTest) { cout <<"Derived::FunTest2()" << endl; } //错误,不是虚函数 void FunTest3(int _iTest1) { cout <<"Derived::FunTest3()" << endl; } //错误,参数列表不一样 virtual void FunTest4(int _iTest1,int _iTest2) { cout<<"Derived::FunTest4()"<<endl; }};
在静态多态的实现中有一个重载,在动态多态中有一个重写,我们也就对继承体系中同名成员函数的关系来个小的总结
重载
1.在同一作用域中
2.函数名字要相同,但是参数列表不同
3.返回值可以不同重写
1.在同一作用域中
2.函数名字,参数列表和返回值都相同,但是有个特例(协变)
3.必须有virtual关键字
4.访问限定符可以不同重定义
1.在不同作用域中
2.函数名字相同
3.在基类和派生类中,只要不构成重写的就是重定义。
纯虚函数
在成员函数(必须为虚函数)的形参列表后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类), 抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。
在一开始我们定义的washroom类中就有纯虚函数
class Person{public: //纯虚函数 virtual void GoToWashRoom(WashRoom & washRoom) = 0;};
剖析虚函数列表
#include <iostream>using namespace std;class C{public: C() { i = 10; cout << "this = " << this << endl; } virtual ~C(){};private: int i;};int main(){ C c; cout << sizeof(c) << endl; system("pause"); return 0;}
如果在类中我们定义的不是虚拟析构函数,是普通析构函数的话,类的大小应该为4,就是类C的成员变量i的大小,
从图中我们可以看到,此类的大小为8,
在类成员变量的上面多出了4个字节的内容,这和我们之前解决菱形问题二义性引入虚拟继承的类似
对于有虚函数的类,编译器都会维护一张虚函数表(虚表),对象的前四个字节是指向虚表的指针(虚表指针)
CTest类的内存布局:
内存2中的00 00 00 00为虚表的结束标志
单继承下的虚表列表
class Base{public:virtual void FunTest0(){cout<<"Base::FunTest0()";}virtual void FunTest1(){cout<<"Base::FunTest1()";}virtual void FunTest2(){cout<<"Base::FunTest2()";}};class Derived:public Base{public:virtual void FunTest4(){cout<<"Derived::FunTest4()" ;}virtual void FunTest5(){cout<<"Derived::FunTest5()" ;}virtual void FunTest6(){cout<<"Derived::FunTest6()" ;}};typedef void (*VirtualFunPtr)();void PrintVirtualTable(Base& b, const string& strInfo){cout<< strInfo<<endl;VirtualFunPtr* pVirtualFun = (VirtualFunPtr*)((*( int *)&b));while(*pVirtualFun){(*pVirtualFun)();cout<< ": "<<pVirtualFun<<endl;pVirtualFun++;}cout<<endl;}int main(){Base b;Derived d;PrintVirtualTable(b, "Base Vpf:");PrintVirtualTable(d, "Derived Vpf:");return 0;}
基类内存布局
基类的虚表
派生类的内存布局
派生类的虚表
由图可知,虚函数是按照其声明顺序存在于虚表中的
在派生类中,前面是基类的虚函数,后面是派生类的虚函数。
这是在没有发生重写的情况下。
下面就来看看重写的情况
lass Base{public: virtual void FunTest0(){ cout << "Base::FunTest0()"; } virtual void FunTest1(){ cout << "Base::FunTest1()"; } virtual void FunTest2(){ cout << "Base::FunTest2()"; }};class Derived :public Base{public: virtual void FunTest4(){ cout << "Derived::FunTest4()"; } virtual void FunTest1(){ cout << "Derived::FunTest1()"; } virtual void FunTest6(){ cout << "Derived::FunTest6()"; }};typedef void(*VirtualFunPtr)();void PrintVirtualTable(Base& b, const string& strInfo){ VirtualFunPtr* pVirtualFun = (VirtualFunPtr*)((*(int *)&b)); while (*pVirtualFun) { (*pVirtualFun)(); cout << ": " << pVirtualFun << endl; pVirtualFun++; } cout << endl;}int main(){ Base b; Derived d; PrintVirtualTable(b, "Base Vpf:"); PrintVirtualTable(d, "Derived Vpf:"); system("pause"); return 0;}
多继承的情况
using namespace std;class Base1{public: Base1(){ _b1 = 1; } virtual void PrintBase1(){ cout << "Base1::PrintBase1()"; } int _b1;};class Base2{public: Base2(){ _b2 = 2; } virtual void PrintBase2(){ cout << "Base2::PrintBase2()"; } int _b2;};class Derived :public Base1, public Base2{public: Derived(){ _d = 3; } virtual void PrintDdrived(){ cout << "Derived::PrintDerived()"; } int _d;};typedef void(*VirtualFunPtr)();void PrintVt(Base1& b, const string& strInfo){ VirtualFunPtr* pVirtualFun = (VirtualFunPtr*)((*(int *)&b)); while (*pVirtualFun) { (*pVirtualFun)(); cout << ": " << pVirtualFun << endl; pVirtualFun++; } cout << endl;}int main(){ Base1 b1; Base2 b2; Derived d; PrintVt(b1, "Base Vpf:"); PrintVt(d, "Derived Vpf:"); return 0;}
其内存布局
从图中我i们可以看到,派生类的虚表是接在第一个继承的基类的后面的。
阅读全文
0 0
- OOP之多态 【C#】
- objective-c之多态
- objective-c之多态
- 【C#】面向对象之多态
- C#——面向对象之多态
- Linux C之多线程
- Objective-c 特性之多态、动态类型和动态绑定
- Objective-c 特性之多态、动态类型和动态绑定
- Objective-c 特性之多态、动态类型和动态绑定
- C+学习之多态篇(虚函数)
- linux c sockset之多播
- linux c sockset之多播
- java之多态,
- 面向对象之多态
- 面向对象之多态
- c++规范之多态
- C++学习之多态
- 面向对象之多态
- 菜鸟学Java(二十三)——Java内存分析
- 弹幕框架
- java内存问题
- AndroidStudio插件GsonFormat快速实现JavaBean
- AccessibilityService使用情况
- C++之多态
- java比较两个相同对象的属性是否一致
- hrbust 2340 一品五彩
- extJs的layout : 'column'布局出错
- Header And contentType
- 基于MHA的MySQL高可用架构的实现
- SQL基础之数据类型
- python语言特性(思维导图)
- string.intern()那些事