多态指的是 父类和子类在执行相同行为的时候,出现了不同的结果。而虚函数是实现多态的重要条件之一。下面以Person类和Student类来了解多态。
class Person {
public:
virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
// 子类的 virtual 可以不写,但是父类的必须写
virtual void BuyTicket() { cout << "买票-半价" << endl; }
};
目录
一、认识多态
1、多态的定义
多态指的是 父类和子类在执行相同行为的时候,出现了不同的结果。成人和学生执行买票这种行为的时候,成人的结果是全票,学生的结果是半票。
实际上为了从语言的角度达到上述效果,其实就是父类Person 和子类Student 有着相同的成员函数 BuyTicket,父类去调用 BuyTicket 的结果是买全票,子类去调用 BuyTicket 的结果是买半票。我们的第一想法可能是这样
Person p;
p.BuyTicket(); // 成人买票
Student s;
s.BuyTicket(); // 儿童买票
但是这样的灵活性不强,于是引入了多态的概念,仅仅使用一个对象,就可以达到两种效果。
void Func(Person& p)
{
p.BuyTicket();
}
int main(){
Person ps;
Student st;
Func(ps); // 传递Person对象买成人票
Func(st); // 传递Student对象买半票
}
2、多态的构成条件
在继承中要构成多态需要满足两个条件:
- 必须通过基类的指针或者引用调用虚函数
- 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
二、虚函数的定义以及虚函数重写
1、什么是虚函数
被 virtual 修饰的类成员函数称为 虚函数,这里的 virtual 跟之前虚继承里用到的 virtual 虽然是一个关键字,但是两者没有任何联系。
2、虚函数重写的条件
要重写虚函数需要满足一定的条件。既然是要让子类的某个函数来覆盖父类原本的函数,那就至少要求,子类的某个函数和父类的某个函数长得一模一样,这样才叫重写。
虚函数重写条件:派生类有一个函数跟基类完全相同的虚函数。(函数名、形参、返回值相同)
3、虚函数重写示例
大前提是两者有继承关系,然后是两个类有完全相同的虚函数(其实子类不加virtual也可)
class Person {
public:
virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
// 子类的 virtual 可以不写,但是父类的必须写
virtual void BuyTicket() { cout << "买票-半价" << endl; }
};
4、虚函数重写的两种特例情况
下面有两种特殊情况即便是不满足上述条件也构成虚函数重写
(1) 协变
协变指的是 基类和派生类的两个虚函数的返回值可以不同,但是返回值必须满足两个条件:
- 返回值类型有继承关系
- 返回值类型必须是指针 或者 引用
class Person {
public:
virtual Person* BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
virtual Student* BuyTicket() { cout << "买票-半价" << endl; }
};
(2) 析构函数的重写
如果基类的析构函数为虚函数,无论是否加 virtual 关键字,都与基类的析构函数构成重写。
class Person {
public:
virtual ~Person() { }
};
class Student : public Person {
public:
virtual ~Student() { } // 与派生类的析构函数构成重写(子类的 virtual 可以不写)
};
这是因为编译器对析构函数的名称进行了特殊处理,编译后析构函数的名称统一处理成 destructor