C++作为面向对象的语言,有三大特性:封装、继承和多态。多态的基础是继承,没有继承就没有多态。那么,什么是多态呢?多态是指相同的对象收到不同的消息时产生不同的动作;或者不同的对象收到相同的消息时产生不同的动作。
一、静态多态(早绑定)
在函数重载时,程序早在编译阶段就已经确定要使用哪个函数,这种情况称为早绑定。
二、动态多态(晚绑定)
前面已经说明多态的基础是继承,那么在接下来的介绍中,这些内容都是关于父类和子类的。若父类和子类定义了同名成员函数,当使用父类的指针指向子类的对象时,如何才能调用子类中的成员函数呢?那就是虚函数!
1、虚函数
虚函数的定义方法为:
virtual fun()
当在父类和子类中的多态函数前都使用关键字virtual修饰时,使用父类的指针实例化子类的对象时,父类的指针调用子类的同名成员函数,会返回子类成员函数的结果。当然,如果子类中的多态函数没有使用virtual修饰,系统会自动添加,一般情况下,推荐修饰,防止子类的子类需要实现动态多态。
2、虚析构函数
当使用父类的指针实例化子类的对象时,使用完成时,只执行父类的析构函数释放父类对象申请的空间,而子类对象的空间无法释放,此时会造成内存泄漏。那么,如何解决呢?那就是使用虚析构函数。
虚析构函数定义方法如下:
//类A的虚析构函数
virtual ~A();
当子类B继承类A时,会继承virtual,建议子类的析构函数也使用virtual修饰。
3、virtual的使用限制
- (1)普通函数不能使用虚函数,否则会出现编译错误。虚函数必须是某个类的成员函数。
- (2)静态成员函数不能是虚函数,否则会出现编译错误。因为静态成员函数(使用static修饰)不属于任何一个对象,它与类同生共死。
- (3)内联函数不能是虚函数。当内联函数使用virtual修饰时,系统会自动忽略inline关键字,而把该函数完全当成是虚函数。
- (4)构造函数不能是虚函数,否则会出现编译错误。
4、覆盖
若子类中未定义与父类同名的虚函数,那么在实例化子类的对象时,子类的虚函数表中存放的是父类的虚函数入口地址;但是,若子类中定义了与父类同名的虚函数,那么在子类的虚函数表中存放的便是子类自身的虚函数的入口地址,这种情况成为覆盖。
5、纯虚函数
由于在C++中关于虚继承、虚函数、纯虚函数的概念容易混淆,在此给出纯虚函数的定义:纯虚函数无函数体,函数名=0,如下:
virtual void fun() = 0;
纯虚函数是某个类的成员函数,包含纯虚函数的类称为抽象类,只包含纯虚函数而不包含其他任何数据成员及成员函数的类称为接口类,接口类通常用于表达一种能力或者协议。
6、RTTI(运行时类型识别)
如有需要,有关typeid 和 dynamic_cast的内容将在之后的博客中详细介绍。