目录
前言
哈喽,小伙伴们大家好。上一章我们一起学习了继承,今天我们继续来学习面向对象三大特性中的最后一个特性——多态。事不宜迟,快拿起小本本,跟我一起开始吧。
一、多态的概念
概念:多态,顾名思义,也就是多种状态。通俗点说就是不同的对象去干同一件事情时会产生不同的状态。以买火车票为例,普通人买票就要付全款,学生买票就可以出半价,军人虽然不能优惠但是可以优先买票。
放到我们程序中就是不同继承关系的类对象去调用同一个函数,会产生不同的状态。
二、多态的定义及实现
1、虚函数的概念
在了解多态的构成条件前,我们先来了解一个概念,虚函数。
虚函数:被virtual修饰的类成员函数称为虚函数。要注意,虽然虚函数的关键字和虚拟继承的关键字相同,但实际它们没有任何关系。
2、虚函数的重写
2.1概念
虚函数的重写(覆盖):派生类中有一个和基类中完全相同的虚函数(函数名,返回值,参数列表都相同),则成为子类对基类的虚函数进行了重写。
注意:如果基类的函数为虚函数,不管派生类的相同函数前加不加virtual,都默认是虚函数,进行了重写。
class Person {
public:
virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
virtual void BuyTicket() { cout << "买票-半价" << endl; }
/*注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写(因
为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范,不建议
这样使用*/
};
2.2虚函数重写的两个例外
虚函数重写一般要严格遵守函数完全相同的条件,但是有两个例外情况下这个规则被打破。
(1)协变(返回值不同)
派生类重写基类的虚函数时,与基类虚函数返回值不同。如果派生类虚函数返回的是派生类对象的引用或指针,基类虚函数返回的是基类对象的引用或指针,则构成协变。这种情况下可以看作进行了虚函数重写。虚函数一般很少使用,了解即可。
class A {};
class B : public A {};
class Person {
public:
virtual A* f() { return new A; }
};
class Student : public Person {
public:
virtual B* f() { return new B; }
};
(2) 析构函数重写(名字不同)
如果基类的析构函数为虚函数,则与派生类的析构函数进行重写。虽然这两个函数看上去名字并不相同,但编译器进行了处理,编译后析构函数的名称统一编译成destructor。、
class Person {
public:
virtual ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person {
public:
virtual ~Student() { cout << "~Student()" << endl; }
};
// 只有派生类Student的析构函数重写了Person的析构函数,下面的delete对象调用析构函
//数,才能构成多态,才能保证p1和p2指向的对象正确的调用析构函数。
int main()
{
Person* p1 = new Person;
Person* p2 = new Student;
delete p1;
delete p2;
return 0;
}
注意:我们在写多态时,尽量不要出现协变,并且基类和派生类的虚函数前面都加上virtual,使代码更规范。
3、多态的构成条件
多态的构成条件:
- 被调用的必须是虚函数,且派生类必须对基类的虚函数进行了重写。
- 必须通过基类的指针或引用调用虚函数。
我们以普通人和学生买票为例,代码如下: