面向对象程序设计的核心思想是数据抽象、继承和动态绑定。 通过使用数据抽象,我们可以将类的接口与实现分离; 使用继承,可以定义相似的类型并对其相似关系建模; 使用动态绑定,可以在一定程度上忽略相似类型的区别,而以统一的方式使用它们的对象。

继承

通过继承(inheritance)联系在一起的类构成一种层次关系。通常在层次关系的根部有一个基类(base class),其他类则直接或间接地从基类继承而来,这些继承得到的类称为派生类(derived class)。基类负责定义在层次关系中所有类共同拥有的成员,而每个派生类定义各自特有的成员。

对于某些函数,基类希望派生类各自定义适合自身的版本,此时基类就将这些函数声明为虚函数。派生类需要对这些操作提供自己的新定义以覆盖(override)从基类继承而来的旧定义。

派生类通过类派生列表明确指出它是从哪个(哪些)基类继承而来。类派生列表的形式是:首先是一个冒号,后面紧跟以逗号分隔的基类列表,其中每个基类前面可以有访问说明符。

动态绑定

通过使用动态绑定(dynamic binding),我们能用同一段代码分别处理基类和派生类的对象。print_total函数传入形参是基类的引用类型,而实际我们既能传基类实参又能传派生类实参。根据实际传入的对象类型将决定到底执行net_price()的哪个版本:如果传的是基类类,则调用Quote::net_price();如果传的是派生类,则调用Bulk_quote::net_price()。因为在上述过程中函数的运行版本由实参决定,即在运行时选择函数的版本,所以动态绑定有时又被称为运行时绑定(run-time binding)。

在C++语言中,当我们使用基类的引用(或指针)调用一个虚函数时将发生动态绑定。

//basic的类型是Quote; bulk的类型是Bulk_quote
print_total(cout, basic, 20); //调用Quote的net price
print_total(cout, bulk, 20); //调用Bulk quote的net_price

成员函数与继承

基类通过在其成员函数的声明语句之前加上关键字virtual使得该函数执行动态绑定。任何构造函数之外的非静态函数都可以是虚函数。关键字virtual只能出现在类内部的声明语句之前而不能用于类外部的函数定义。如果基类把一个函数声明成虚函数,则该函数在派生类中隐式地也是虚函数。

成员函数如果没被声明为虚函数,则其解析过程发生在编译时而非运行时。

class Quote {
  public:
    Quote() = default;
    Quote(const std::string &book, double sales_price) : bookNo(book), price(sales_price) {}
    std::string isbn() const { return bookNo; };
    virtual double net_price(std::size_t n) const { return n * price; };
    virtual ~Quote() = default;
 
  private:
    std::string bookNo;
 
  protected:
    double price = 0.0;
};
 
class Bulk_quote : public Quote {
  public:
    Bulk_quote() = default;
    Bulk_quote(const std::string &book, double p, std::size_t qty, double disc)
        : Quote(book, p), min_qty(qty), discount(disc) {}
    double net_price(std::size_t) const override;
 
    double print_total(std::ostream &os, const Quote &item, size_t n) {
        double ret = item.net_price(n);
        os << "ISBN: " << item.isbn() << " # sold: " << n << " total due: " << ret << std::endl;
        return ret;
    }
 
  private:
    std::size_t min_qty = 0;
    double discount = 0.0;
};
 
double Bulk_quote::net_price(std::size_t cnt) const {
    if (cnt >= min_qty) {
        return cnt * (1 - discount) * price;
    } else {
        return cnt * price;
    }
}