多态
多态就是指一种事物在不同时期的不同体现。
多态有两张情况,动态多态和静态多态
静态多态
在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推断出要调用那个函数,如果有对应的函数就调用该函数,否则出现编译错误。
静态多态的实现方法有两种:
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们可以看到,派生类的虚表是接在第一个继承的基类的后面的。