面向对象程序设计
概述
核心思想:封装、继承、多态。函数的运行版本由实参决定,即在运行时选择函数的版本,所以动态绑定有时被称为运行时绑定。
定义基类和派生类
- 基类通常都应该定义一个虚析构函数,即使该函数不执行任何实际操作也是如此。
- 任何构造函数之外的非静态函数都可以是虚函数。
- 派生类列表中的访问说明符的作用是控制派生类从基类继承而来的成员是否对派生类的用户可见。
- 如果派生类没有覆盖基类中的某个虚函数,则该虚函数的行为类似其他的普通成员,派生类会直接继承其在基类中的版本。
- 每个类控制它自己的成员初始化过程。
- 必须明确每个类负责定义各自的接口,要想与类的对象交互必须使用该类的接口,即使这个对象是派生类的基类部分也是如此。
- 表达式的静态类型在编译时总是已知的,它是变量声明时的类型或表达式生成的类型;动态类型则是变量或表达式表示的内存中的对象的类型,动态类型直到运行时才可知。
- 不存在从基类向派生类的隐式类型转换,在对象之间不存在类型转换。
- 当我们用一个派生类对象为一个基类对象初始化赋值时,只有该派生类对象中的基类部分会被拷贝、赋值或移动,它的派生类部分将被切掉。
- 和任何其他成员一样,派生类向基类的类型转换也可能会由于访问受限而变得不可行。
虚函数
- 当且仅当对通过指针或引用调用虚函数时,才会在运行时解析该调用,也只有在这种情况下对象的动态类型才有可能与静态类型不同。
- 一旦某个函数被声明为虚函数,则在所有派生类中它都是虚函数。
- 如果某次函数调用使用了默认实参,则该实参值由本次调用的静态类型决定。
抽象基类
- 纯虚函数,可以为纯虚函数提供定义,不过函数体必须定义在类的外部。
double net_price(std::size_t) const = 0;
- 不能创建抽象基类的对象
访问控制与继承
- 派生类的成员或友元只能通过派生类对象来访问基类的受保护成员。派生类对于一个基类对象中的受保护成员没有访问权限。只能访问其基类部分的受保护成员。
- 派生访问说明符还可以控制继承自派生类的新类的访问权限。
- · 只有当D公有地继承B时,用户代码才能使用派生类向基类的转换;如果D继承B的方式是受保护的或者私有的,则用户代码不能使用该转换。· 不论D以什么方式继承B,D的成员函数和友元都能使用派生类向基类的转换;派生类向其直接基类的类型转换对于派生类的成员和友元来说永远是可访问的。· 如果D继承B的方式是公有的或者受保护的,则D的派生类的成员和友元可以使用D向B的类型转换;反之,如果D继承B的方式是私有的,则不能使用。
- 不能继承友元关系;每个类负责控制各自成员的访问权限。
- 派生类只能为那些它可以访问的名字提供using声明。
继承中的类作用域
- 当存在继承关系时,派生类的作用域嵌套在其基类的作用域之内。
- 派生类的成员将隐藏同名的基类成员,名字查找先于类型检查。
构造函数与拷贝控制
- 当我们确实需要执行移动操作时应该首先在基类中进行定义。
- 如果构造函数或析构函数调用了某个虚函数,则我们应该执行与构造函数或析构函数所属类型相对应的虚函数版本。
- 和普通成员的using声明不一样,一个构造函数的using声明不会改变该构造函数的访问级别。
术语
定义基类和派生类
- 成员函数与继承
- 访问控制与继承
- 派生类中的虚函数
- 派生类对象及派生类向基类的类型转换
- 派生类构造函数
- 派生类使用基类的成员
- 关键概念:遵循基类的接口
- 继承与静态成员
- 派生类的声明
- 被用作基类的类
- 防止继承的发生
- 静态类型与动态类型
- 关键概念:存在继承关系的类型之间的转换规则
虚函数
- 对虚函数的调用可能在运行时才被解析
- 关键概念:C++的多态性
- 派生类中的虚函数
- final和override说明符
- 虚函数与默认实参
- 回避虚函数的机制
抽象基类
- 纯虚函数
- 含有纯虚函数的类是抽象基类
- 派生类构造函数只初始化它的直接基类
- 关键概念:重构
访问控制与继承
- 受保护的成员
- 公有、私有和受保护继承
- 派生类向基类转换的可访问性
- 关键概念:类的设计与受保护的成员
- 友元与继承
- 改变个别成员的可访问性
- 默认的继承保护级别
继承中的类作用域
- 在编译时进行名字查找
- 名字冲突与继承
- 通过作用域运算符来使用隐藏的成员
- 关键概念:名字查找与继承
- 一如往常,名字查找先于类型检查
- 虚函数与作用域
- 通过基类调用隐藏的虚函数
- 覆盖重载的函数
构造函数与拷贝控制
- 虚析构函数将阻止合成移动操作
- 派生类中删除的拷贝控制与基类的关系
- 移动操作与继承
- 定义派生类的拷贝或移动构造函数
- 派生类赋值运算符
- 派生类析构函数
- 在构造函数和析构函数中调用虚函数
- 继承的构造函数的特点