- 继承(Inheritance)
- 允许在保持原有类特性的基础上进行扩展,增加功能。这样产生的新类称为“派生类”。继承呈现了面向对象程序设计的层次结构,体现了人类对现实世界由简单到复杂的认识过程。
- 多态性(Polymorphism)
- 包括静态的多态性和动态的多态性。前者亦称编译时的多态性,包括函数重载和运算符重载。多态体现了类推和比喻的思想方法。
继承
-
被继承的类型称为基类(base class)或超类(superclass),新产生的类为派生类(derived class)或子类(subclass)。基类和派生类的集合称作类继承层次结构(hierarchy)。如果基类和派生类共享相同的公有接口,则派生类被称作类的子类型(subtype)。
-
继承机制体现了现实世界的层次结构,如下图所示。
- 单一继承(single-inheritance) :一个派生类只有一个直接基类。
- 多重继承(multiple-inheritance) :一个派生类可以同时有多个基类。
编制派生类的步骤
- 吸收基类的成员:不论是数据成员,还是函数成员,除构造函数与析构函数外全盘接收(继承方式)。
- 改造基类成员:声明一个和某基类成员同名的新成员,该新成员将屏蔽基类同名成员。称为同名覆盖(override)
- 新成员若是成员函数,参数表也必须一样,否则是重载。
- 发展新成员:派生类新成员必须与基类成员不同名,它的加入保证派生类在功能上有所发展。
- 重写构造函数与析构函数。
公有派生与私有派生
-
派生类定义时的访问限定符,是指访问控制,亦称为继承方式,用于在派生类中对基类成员进一步的限制。
-
访问控制也是三种:公有(public)方式,保护(protected)方式和私有(private)方式,相应的继承亦称公有继承、保护继承和私有继承。访问限定有两方面含义:
- 派生类新增成员函数对基类(继承来的)成员的访问;
- 从派生类对象之外对派生类对象中的基类成员的访问。
虚基类
- 关键字:
virtual
- 类A派生出类B和类C,类D继承自类B和类C,这个时候类A中的成员变量和成员函数继承到类D中变成了两份,一份来自 A–>B–>D 这一路,另一份来自 A–>C–>D 这一条路。
- 为了解决这个问题,C++提供了虚基类,使得在派生类中只保留间接基类的一份成员。
结果如下:
多态
-
C++中,实现多态有以下方法:虚函数,抽象类,覆盖,模板(重载和多态无关)。
- 在C++中有两种多态性:
- 编译时的多态性:通过函数的重载和运算符的重载来实现的。
- 运行时的多态性:在程序执行前,无法根据函数名和参数来确定该调用哪一个函数,必须在程序执行过程中,根据具体情况来动态地确定。它是通过类继承关系和虚函数来实现的,目的也是建立一种通用的程序。
- 虚函数是一个类的成员函数,定义格式:
virtual 返回类型 函数名(参数表);
- 当在派生类中重新定义虚函数(overriding a virtual function,亦译作超载或覆盖)时,不必加关键字virtual。但重新定义时不仅要同名,而且它的参数表和返回类型全部与基类中的虚函数一样,否则联编时出错。
- 只有类的成员函数才能说明为虚函数。这是因为虚函数仅适用于有继承关系的类对象。
- 静态成员函数,是所有同类对象共有,不受限于某个对象,不能作为虚函数。
- 内联函数每个对象一个拷贝,无映射关系,不能作为虚函数。
- 析构函数可定义为虚函数,构造函数不能定义虚函数,因为在构造函数时对象还没有完成实例化。在基类中及其派生类中都动态分配的内存空间时,必须把析构函数定义为虚函数,实现撤消对象时的多态性。
- 虚函数执行速度要稍慢一些。因为,为了实现多态性,每一个派生类中均要保存相应虚函数的入口地址表,函数的调用机制也是间接实现。所以多态性总是要付出一定代价,但通用性是一个更高的目标。
- 为了方便,你可以只将基类中的函数声明为虚函数,所有派生类中具有覆盖关系的同名函数都将自动成为虚函数。
函数多态
也就是我们常说的函数重载(function overloading)。基于不同的参数列表,同一个函数名字可以指向不同的函数定义:
宏多态
- 带变量的宏可以实现一种初级形式的静态多态:
动态多态 && 静态多态
- 动态多态,众所周知的多态,使用虚函数构建
- 静态多态,使用模版构建
抽象类的纯虚函数
纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加”=0” ,同 java中抽象方法类似
virtual void funtion1()=0
。C++接口使用抽象类来实现
这将产生以下结果:
Tips
- 用C++开发的时候,用来做基类的类的析构函数一般都是虚函数
-
这样做是为了当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。
-
然而,不是所有类的析构函数都要写成虚函数。因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样会增加类的存储空间。故,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。
-
- 关于多态:
- 编译时:模板、重载
- 运行时:通过虚表
- 运行时多态相关的概念:静态类型、动态类型。