继承与虚函数
基类与派生类也叫父类与子类。派生类拥有基类的全部成员(属性和函数),当时需要按照基类中设置的访问权限访问。
-
函数
在一个类中的非静态成员函数可以分为两类:
-
普通成员函数
- 被派生类直接继承,不需要改变。
- 编译时解析。
- 派生类可以覆盖(隐藏)它。
-
虚函数
-
基类希望派生类覆盖。
-
除构造函数外,所有的非静态函数都可以被声明为虚函数。
-
运行时解析。
-
-
-
非虚函数的重载与虚函数
class Base { public: virtual void run() { ... } }; class D1: public Base { public: void run(int a) { ... } // 隐藏基类虚函数 }; class D2: public D1 { public: void run(int a) { ... } // 隐藏非虚函数 void run() { ... } // 覆盖虚函数 };
-
类型转化
-
指针和应用
- 派生类向基类转换是隐式的
- 基类向派生类转化,需要调用请求类型转化(static_cast或dynamic_cast)
-
对象
-
派生类向基类转化,会切割掉派生类的成员,保留基类成员
-
基类向派生类转化,编译报错
-
-
-
派生类构造函数
每个类负责自己成员的初始化,派生类初始化基类成员,需要调用基类的构造函数(默认调用基类的默认构造函数)。
-
基类静态成员
派生类可以访问基类的静态成员,但是需要符合访问权限。
-
动态绑定
动态绑定发生在对一个指针或者引用调用虚函数。
- 动态绑定 = 指针/引用 + 覆盖(override)虚函数
- 静态绑定 = 对象 + 虚函数;对象 + 非虚函数;指针/引用 + 非虚函数
-
虚函数的默认参数
在调用一个虚函数的时候,虚函数默认参数的值由调用的静态类型决定。
class A { public: virtual void print(int a = 10) {...} }; class B: public A { public: void print(int a = 20) {...} }; int main() { A *a = new B; a->print(); // 默认参数为10 ... }
-
回避虚函数机制
使用作用域运算符 :: 可以强制使用虚函数的某个实现。
-
名字查找与继承
p->mem(); 该调用的查找步骤: 1. 确定p的静态类型 2. 在静态类型的继承链中由下至上的查找名字为mem的函数,找不到则报错。 3. 找到mem后,进行类型检查。 3.1 mem是虚函数且通过指针或引用调用的该函数,则依据动态类型调用虚函数的版本。 3.2 mem不是虚函数或者通过对象调用的该函数,则进行常规函数调用。