# 第十五章 面向对象程序设计
**三个基本概念:数据抽象、继承和动态绑定**
##15.1 OOP概述
使用继承,可以定义相似的类型并对其相似关系建模
使用动态绑定,可以在一定程度上忽略相似类型的却别,而以统一的方式使用他们的对象
####继承
通过继承联系在一起的类构成一种层次关系。通常在层次关系的根部有一个**基类**,其他类则直接或间接地从基类继承而来,这些继承得到的类成为**派生类**
* 基类将 类型相关的函数 与 派生类不做改变直接继承的函数 区分对待
* 某些函数,基类希望派生类各自定义自己的版本,就将函数声明成**虚函数**virtual
* 派生类必须使用类派生列表明确指出基类。派生列表中基类的前面可以有访问说明符:public/protected/private
```
class Quote{
public:
string isbn() const;
virtual double net_price(size_t n) const;
};
class bilk_quote : public Quote{
public:
double net_price(size_t) const override;
};
```
**用逗号来分隔基类列表**
* 派生类必须对所有重新定义的虚函数进行声明,可以显式注明出来改写的是哪个虚函数:加override
####动态绑定
用同一段代码分别处理Quote和bulk_quote
```
double print(ostream& os,const Quote &item,size_t n){
.......
}
```
因为第二个形参是**_Quote的引用_**,所以也可以用bulk_quote调用这个函数,里面的成员函数就是用bulk_quote的版本了。
**在运行时选择函数的版本**
##15.2 定义基类和派生类
###15.2.1 定义基类
1. 作为继承关系中根节点的类通常都会定义一个虚析构函数,所以基类也要定义
2. 当我们使用**指针或引用**调用虚函数时,该调用将被动态绑定。根据引用或指针所绑定的对象的不同,执行不同类的版本
3. 任何构造函数之外的非静态函数都可以是虚的。
4. virtual只能出现在类内部的声明之前,不能出现在外部。基类一个函数是virtual的,则该函数在派生类中也是virtual的
5. 派生类的成员函数不一定有权访问从基类继承而来的成员。派生类能访问公有成员,不能访问私有成员。同时,基类希望派生类(内部)有权访问该成员,同时禁止其他用户访问,就弄protected:
###15.2.2 定义派生类
我们能将派生类型的对象绑定到其基类的**引用或指针**上,
只继承自一个类的形式的继承被称作“单继承”
####虚函数
如果派生类没有覆盖基类的虚函数,那么就直接继承其在基类中的版本
覆盖的话,在const后面/引用限定符后面添加关键字override
####派生类向基类的类型转换
* 派生类自己的部分和继承的部分不一定是连续储存的
* 我们能将指针和对象绑定到派生类对象中的**基类部分**上
* 这种转换叫**派生类到基类的类型转换**,会隐式转换
####派生类构造函数
* 不能直接初始化继承来的成员,需要使用基类的构造函数
```
bulk_quote(const string& book, double p, size_t qty, double disc)
: Quote(book, p), min_qty(qty), discount(disc) {}
```
将前两个参数传递给quote的构造函数。
首先初始化基类部分,然后按照声明顺序依次初始化派生类的成员
####派生类使用基类成员
可以访问public和protected成员
**派生类的作用域嵌套在基类作用域之内**
####静态成员
如果基类定义了一个静态成员,那么在整个继承体系中只有一个该成员
如果是可访问的,我们可以使用基类或者是派生类使用它
####声明
派生类的声明不需要派生列表
####被用作基类的类
如果想将某类用作基类,那么必须已经定义而不是声明
一个派生类拥有**直接基类**和**间接基类**的子对象
####如果想防止继承,在类名后加一个final
`class NoDerived final {};`
###15.2.3 类型转换和继承
静态类型:总是已知的,它是变量声明时的类型或表达式生成的类型
动态类型:变量或表达式表示的内存中对象的类型,直到程序运行时才可知
如果表达式不是引用和指针,那么动态类型就永远和静态类型一致
####不存在从基类向派生类的隐式类型转换,因为基类没有派生类的成员
##15.3 虚函数
所有的虚函数都必须有定义