基类指针、虚纯虚函数、多态性、虚析构
多态
基类指针
// 父类指针可以 new 一个子类对象
Human *pman = new Man();
Human *pwman = new Wonan();
抛出问题:父类指针没有办法调用子类的成员函数,那么你为什么还让父类指针 new 一个子类对象呢?
下面与虚函数搭配
虚函数(动态绑定)
我们只定义一个对象指针,就能够调用父类,以及各个子类的同名函数? ===> 有,这个对象指针,它的类型必须是父类类型
对这个函数有要求:
- 父类中,函数声明之前要加 virtual 声明成虚函数
- 一旦某个函数(在基类)被声明成了虚函数,那么所有派生类(子类)中同名函数都是虚函数
Human *phuman = new Men();
phuman->eat(); // 调用的是 Men 类的 eat 函数
delete phuman; // new 的对象使用完之后要 delete,防止内存泄漏
phuman = new Women();
phuman->eat(); // 调用的是 Women 类的 eat 函数
delete phuman;
phuman = new Human();
phuman->eat(); // 调用的是 Human 类的 eat 函数
delete phuman;
override
注意:为了避免你在子类中写错虚函数,在 C++11 中,你可以在函数声明这里增加一个 override 关键字
这个关键字在 "子类" 中,而且是子虚函数专用
override就是用来说明派生类中的虚函数,你用了这个关键字之后,编译器就会认为你这个 函数就是覆盖了父类中的同名函数。
只有虚函数才存在子类可以覆盖父类中同名函数的问题,那么编译器就会在父类中找同名同参的虚函数,如果没找到,编译器就会报错、
final
final 也是虚函数专用,是用在父类,如果我们在父类的函数声明中加了 final,那么任何尝试覆盖该函数的操作都将引发错误。
virtual void eat() final;
多态性
系统内部实际上是要查一个虚函数表,找到 eat() 的入口地址,从而调用父类或者子类的 eat() 函数,这就是运行时期的多态性
纯虚函数【类似 Java 中的接口 interface】===> 函数原型后增加 = 0
纯虚函数:没有函数体,只有一个函数声明
virtual void eat() = 0;
是在基类中声明的虚函数,但是它在基类中没有定义,但是要求任何派生类都要定义该虚函数自己的实现方法
注意:一旦一个类中有纯虚函数了,那么你就不能生成这个类的对象了【Java 中 interface 不能生成对象】
抽象类不能用来生成对象,主要目的用来统一管理子类对象
基类的析构函数一般写成虚函数(虚析构函数)
// 1. 声明一个 men 对象
Men men;
// 2. 写法2:使用 new
Men *pmen = new Men();
delete pmen; // 自己 new 的对象,必须使用 delete 调用析构函数
有一个问题
Human *phuman = new Men(); // 父类指针指向 子类对象
delete phuman; // 没有执行子类的析构函数
结论:用基类指针 new子类的对象,在 delete 的时候,系统不会调用派生类的析构函数,这肯定就有问题了
解决方式:
- 父类的析构函数写成虚函数
- 继承时候使用 public