多态:一词最初来源于希腊语,意思是具有多种形式或形态的情形。通俗的讲,水在不同的温度下所呈现的不同的状态,比如:水在零摄氏度以下是固态的,在0~100度之间是液态的,在100度以上是气态的。
在C++语言中多态有着更广泛的含义
【静态多态】:
int Add(int a, int b)
{
return a + b;
}
float Add(float a, float b)
{
return a + b;
}
int main()
{
cout << Add(30, 20) << endl;
cout << Add(11.10f, 22.20f) << endl;
return 0;
}
运行结果:
静态多态:编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推断出要调用那个函数,如果有对应的函数就调用该函数,否则出现编译错误。
【动态多态】:
动态绑定:在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应的方法。
使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定。
下来咱们举一个简单的例子:
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();
return 0;
}
运行结果:
【动态绑定条件】
1、通过基类类型的引用或者指针调用虚函数
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;
}
};
int main()
{
Base* pBase = new Derived;
pBase->FunTest1(0);
pBase->FunTest2(0);
pBase->FunTest3(0);
pBase->FunTest4(0);
pBase->FunTest4(0);
return 0;
}
运行结果:
继承体系同名成员函数的关系:
那些成员函数不能定义成虚函数?
(1).友元函数
(2).构造函数
(3).静态成员函数(static)
(4).内联函数(inline)
注:
(1)最好将基类中的析构函数给成virtual;
(2)最好不要使基类的运算符重载给成virtual
总结:
1、派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同(协变除外)。
2、基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
3、只有类的非静态成员函数才能定义为虚函数,静态成员函数不能定义为虚函数。
4、如果在类外定义虚函数,只能在声明函数时加virtual关键字,定义时不用加。
5、构造函数不能定义为虚函数,虽然可以将operator=定义为虚函数,但最好不要这么做,使用时容易混淆。
6、不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会出现未定义的行为。
7、最好将基类的析构函数声明为虚函数。(析构函数比较特殊,因为派生类的析构函数跟基类的析构函数名称不一样,但是构成覆盖,这里编译器做了特殊处理)
8、虚表是所有类对象实例共用的