参考资料:
- 《C++ Primer》第5版
- 《C++ Primer 习题集》第5版
15.1 OOP:概述(P526)
**面向对象程序设计(object-oriented programming)**的核心思想是数据抽象、继承和动态绑定。
继承
通过继承(inheritance)联系在一起的类构成一种层次关系,在层次关系根部有一个基类(base class),从基类继承而来的类称为派生类(derived class)。基类负责定义在层次关系中所有类的共同成员,每个派生类定义各自独有的成员。
我们定义一个名为 Quote
的类,表示按原价销售的数据,并将它作为层次关系的基类。Quote
派生出另一个名为 Bulk_quote
的类,表示可以打折销售的书籍:
class Quote {
public:
string isbn() const;
virtual double net_price(size_t n) const;
};
class Bulk_quote :public Quote {
public:
double net_price(size_t n) const override;
};
在 C++ 语言中,基类将类型相关的函数(如 isbn
)与派生类不做改变直接继承的函数(如 net_price
)区分对待。对于某些函数,基类希望它的派生类各自定义适合自身的版本,此时基类就将这些函数声明成虚函数(virtual function)
派生类必须通过使用类派生列表(class derivation list)明确指出它从哪些类继承而来。类派生列表的形式是:首先是一个冒号,后面紧跟以逗号分隔的基类列表,每个基类前面可以有访问说明符。派生类必须对所有重新定义的虚函数进行声明,派生类可以选择在这样的函数之前加上 virtual
关键字,但不是必须的。C++11 新标准允许派生类显式注明使用哪个成员函数改写基类的虚函数,方式是在函数的形参列表增加 override
关键字。
动态绑定
通过动态绑定(dynamic binding),我们能用同一段代码分别处理 Quote
和 Bulk_quote
的对象:
double print_total(ostream &os,
const Quote &item, size_t n) {
// 调用Quote::net_price或者Bulk_quote::net_price
double ret = item.net_price(n);
os << "ISBN: " << item.isbn()
<< " # sold: " << n << "total due: " << ret << endl;
return ret;
}
对于上面的函数,由于其 item
形参是基类 Quote
的一个引用,所以我们既能使用 Quote
对象,也能使用 Bulk_quote
对象调用该函数;因为 print_total
使用引用类型调用 net_price
,所以实际传入 print_total
的对象类型将决定执行 net_price
的哪个版本。
在上述过程中,函数的运行版本由实参决定,即在运行时选择函数版本,所以动态绑定有时也称为运行时绑定。
15.2 定义基类和派生类(P527)
15.2.1 定义基类(P528)
我们首先完成 Quote
类的定义:
class Quote {
public:
Quote() = default;
Quote(string &book, double sales_price):
bookNo(book), price(sales_price) {
}
string isbn() const {
return bookNo; }
virtual double