一、多态的概念
1.1 概念
- 通俗的来说,多态就是多种形态,具体一点就是,去完成某个行为时,不同的对象去完成会有不同的状态。
- 我们来举个栗子,比如说我们平时买票这个行为,普通人买票就是全价票,学生就是半价票,军人可以优先买票,这就相同的一个行为,不同的人去完成有不同的效果。
二、多态的定义和实现
2.1 多态的构成条件
- 1.调用函数的对象必须是指针或者引用
- 2.被调用的函数必须是虚函数,且完成了虚函数的重写
2.2 什么是虚函数
- 虚函数就是在类的成员函数之前加上关键字virtual
class Person
{
public:
virtual void BuyTicket()
{
cout << "买票-全价" << endl;
}
};
2.3 什么是虚函数的重写
- 虚函数的重写也成为覆盖:派生类有一个和基类完全相同的虚函数,我们就称派生类重写了基类的虚函数,完全相同指:函数名,参数,返回值都相同。
举个栗子看看
class Person
{
public:
virtual void BuyTicket()
{
cout << "买票-全价" << endl;
}
};
class Student : public Person
{
public:
virtual void BuyTicket()
{
cout << "买票-半价" << endl;
}
};
//此处必须传指针或是引用,若传对象则不构成多态
//构成多态,调那个类中的函数取决于对象
//不构成多态,调那个类中的函数取决于类型
void Func(Person& p)
{
p.BuyTicket();
}
int main()
{
Person Mike;
Func(Mike);
Student Jack;
Func(Jack);
return 0;
}
2.4 虚函数重写中的例外:协变
- 虚函数重写中有一个例外::重写的虚函数的返回值可以不同,但是必须分别是基类指针和派生类指针或者基类引用和派生类引用。
举个栗子简单看一下,了解即可,并不多用
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.5 不规范的重写行为
- 在派生类中重写的成员函数可以不加virtual关键字,也是构成重写的,因为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性,我们只是重写了它。但是这样书写很不规范,也容易造成误解,所以最好不要如此书写。
析构函数的重写行为
- 我们在重定义的时候也有提到关于析构函数的特殊性,基类中的析构函数如果是虚函数,那么派生类的析构函数就重写了基类的析构函数。
- 这里他们的函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor。
- 可以参考博客 https://blog.youkuaiyun.com/chenxiyuehh/article/details/88747751
- 由于有这个特殊性,C++中建议最好将基类的析构函数写成虚函数
接口继承和实现继承
普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数
三、抽象类
3.1 纯虚函数
-在虚函数的后面写上 =0 ,则这个函数为纯虚函数。virtual void Drive() = 0;
3.2 抽象类
- 包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
- 我们可以说,其实虚函数是一种强制派生类重写虚函数的机制,因为派生类若不重写虚函数,则无法实例化出对象。
- 抽象类,描述世界上一些比较泛化不具体的类型,这些类型找不出具体的对象,如人,动物,是一些比较宽泛的类。
还是举个栗子看看
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;
}
};
void Test()
{
Car* pBenz = new Benz;
pBenz->Drive();
Car* pBMW = new BMW;
pBMW->Drive();
}
3.3 C++11 override 和 final
另外补充一下的是C++11提供override 和 final 来修饰虚函数。
实际中我们建议多使用纯虚函数+ overrid的方式来强制重写虚函数,因为虚函数的意义就是实现多态,如果没有重写,虚函数就没有意义。
final
class Car
{
public:
virtual void Drive() final {}
};
class Benz :public Car
{
public:
virtual void Drive() { cout << "Benz-舒适" << endl; }
};