多态,通俗来讲就是多种形态。多态分为编译时多态(静态多态)和运⾏时多态(动态多态)。静态多态主要就是函数重载和函数模板,他们传不同类型的参数就可以调⽤不同的函数,通过参数不同达到多种形态,之所以叫编译时多态,是因为他们实参传给形参的参数匹配是在编译时完成的,我们把编译时⼀般归为静态,运行时归为动态。
而运行时多态,通俗来讲就是一个行为(函数),当传递不同的对象时会进行不同的操作。就比如买票时,普通人买时全价,学生买是半价,军人则是优先买票。同样是买票的行为,不同的人对应的操作却不一样,这就是运行时多态。
多态是⼀个继承关系的下的类对象,去调⽤同⼀函数,产⽣了不同的⾏为。⽐如Student继承了Person。Person对象买票全价,Student对象优惠买票。
1、必须是指针或者引用调用虚函数
2、被调用的函数必须是虚函数
说明:要实现多态效果,第⼀必须是基类的指针或引⽤,因为只有基类的指针或引⽤才能既指向派⽣类对象;第⼆派⽣类必须对基类的虚函数重写/覆盖,重写或者覆盖了,派⽣类才能有不同的函数,多态的不同形态效果才能达到。
那什么是虚函数呢?在类成员函数前加关键字virtual,该成员函数就称为虚函数,注意,非类成员函数不能加virtual修饰。
- class Person
- {
- public:
- virtual void BuyTicket() { cout << "买票-全价" << endl;}
- };
虚函数的重写/覆盖:派⽣类中有⼀个跟基类完全相同的虚函数(即派⽣类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称派⽣类的虚函数重写了基类的虚函数。这样才能根据不同的对象执行不同的行为。
- class Person {
- public:
- virtual void BuyTicket() { cout << "买票-全价" << endl; }
- };
- class Student : public Person {
- public:
- virtual void BuyTicket() { cout << "买票-打折" << endl; }
- };
在重写基类虚函数时,派⽣类的虚函数在不加virtual关键字时,虽然也可以构成重写(因为继承
后基类的虚函数被继承下来了在派⽣类依旧保持虚函数属性),但是该种写法不是很规范,不建议这样使⽤。
完整代码:
- class Person {
- public:
- virtual void BuyTicket() { cout << "买票-全价" << endl; }
- };
-
- class Student : public Person {
- public:
- virtual void BuyTicket() { cout << "买票-打折" << endl; }
- };
-
- void Func(Person* ptr)//这里是基类的指针调用虚函数
- {
- // 这⾥可以看到虽然都是Person指针Ptr在调⽤BuyTicket
- // 但是跟ptr没关系,⽽是由ptr指向的对象决定的。
- ptr->BuyTicket();
- }
-
- int main()
- {
- Person ps;
- Student st;
- Func(&ps);
- Func(&st);
- return 0;
- }
此时运行结果就为:买票—全价(换行)买票——打折
关于虚函数的重写,还有一点需要注意的是基类的析构函数最好也写成虚函数。这样不管子类的析构是否加virtual,都构成重写。虽然两个类的析构函数名称好像不太一样,但是经过编译器统一处理后,析构函数名称统一编程destructor,构成重写。
下⾯的代码我们可以看到,如果~A(),不加virtual,那么delete p2时只调⽤的A的析构函数,没有调⽤B的析构函数,就会导致内存泄漏问题,因为~B()中在释放资源。
- class A
- {
- public:
- virtual ~A()
- {
- cout << "~A()" << endl;
- }
- };
-
- class B : public A {
- public:
- ~B()
- {
- cout << "~B()->delete:"<<_p<< endl;
- delete _p;
- }
- protected:
- int* _p = new int[10];
- };
- // 只有派⽣类Student的析构函数重写了Person的析构函数,下⾯的delete对象调⽤析构函数,才能
- 构成多态,才能保证p1和p2指向的对象正确的调⽤析构函数。
- int main()
- {
- A* p1 = new A;
- A* p2 = new B;
- delete p1;
- delete p2;
- return 0;
- }
在虚函数的后⾯写上 =0 ,则这个函数为纯虚函数,纯虚函数不需要定义实现(实现没啥意义因为要被派⽣类重写,但是语法上可以实现),只要声明即可。包含纯虚函数的类叫做抽象类,抽象类不能实例化出对象,如果派⽣类继承后不重写纯虚函数,那么派⽣类也是抽象类。纯虚函数某种程度上强制了派⽣类重写虚函数,因为不重写实例化不出对象。
- class Car
- {
- public:
- virtual void Drive() = 0;
- };
- class Benz :public Car
- {
- public:
- virtual void Drive()
- {
- cout << "Benz-舒适" << endl;
- }
- };
- class BMW :public Car
- {
- public:
- virtual void Drive()
- {
- cout << "BMW-操控" << endl;
- }
- };
- int main()
- {
- // 编译报错:error C2259: “Car”: ⽆法实例化抽象类
- Car car;
- Car* pBenz = new Benz;
- pBenz->Drive();
- Car* pBMW = new BMW;
- pBMW->Drive();
- return 0;
- }