参考:C++ Primer 中文版第5版
1. 继承
通过继承(inerutance)联系其他在一起的类构成一种层次关系。通常在层次关系的根部有一个基类(base class),其他类直接或者间接从基类继承而来,这些继承得到的类称为派生类(derived class)。
例子:首先定义一个名为Quote的类,将其作为层次关系中的基类。Quoto的对象表示按原价销售的书籍。Quote派生出另一个名为Bulk_quote的类,表示可以打折销售的书籍。
这些类包含以下两个成员函数:
(1) isbn():返回书籍的ISBN编号。该操作不涉及派生类的特殊性,因此只定义在Quote类中。
(2) net_price(size_t),返回书籍的实际销售价格,前提是用户购买该书的数量达到一定标准,这个操作显然是类型相关的,Quote和Bulk_quote都应该包含该函数。
在C++语言中,基类将类型相关的函数与派生类不做改变直接继承的函数区分对待。对于某些函数,基类希望它的派生类各自定义适合自身的版本,此时基类就将这些函数声明为虚函数(virtual function)。
我们将Quote类编写为:
class Quote { public: std::string isbn() const; virutal double net_price(std::size_t n) const; //常成员函数 //常成员函数用来操作常量或者常对象。 };
派生类必须通过使用类派生列表(class derivative list)明确指明它从哪个基类继承而来。
派生类列表形式:
class Bulk_quote :public Quote { //Bulk_quote继承了Quote //由于使用了public关键字,因此我们完全可以把Bulk_quote的对象当成 //Quote的对象来使用。 public: double net_price(std::size_t) const override; };
派生类必须在其内部对所有重新定义的虚函数进行声明。派生类可以在这样的函数之前加上virtual关键字。
C++11新标准允许派生类显式地注明它将使用哪个成员函数改写基类的虚函数。 具体是在该函数的形参列表后增加一个override关键字。
2. 动态绑定
通过使用动态绑定,我们能用同一段代码分别处理Quote和Bulk_quote的对象。
例如:
double print_total(ostream &os, const Quote &item, size_t n ) //根据传入item形参的对象类型调用Quote::net_price //或者Bulk_quote::net_price double ret = item.net_price(n); os << "ISBN: " <<item.isbn() //调用Quote::isbn << " # sold: "<< n << " total due: " << ret << endl; return ret; }
关于上述代码:
(1) 因为函数print_total的item形参是基类Quote的一个引用,我们既能使用基类的对象调用该函数,也能使用派生类的Bulk_quote的对象调用它。
(2)因为print_total是使用引用类型调用net_price函数,实际传入print_total的对象类型将决定到底执行net_price的哪个版本。
//basic类型是Quote;bulk的类型是Bulk_quote print_total(cout,basic, 20); //调用Quote的net_price print_total(cout, bulk, 20); //调用Bulk_quote的net_price
上述过程中的函数运行版本由实参决定,即在运行时选择函数的版本,所以动态绑定有时又被称之为运行时绑定(run-time binding).
上述函数通过其名为item的参数来进一步调用net_price,其中item的类型是Quote&。因为item是引用而且net_price是虚函数,所以我们调用net_price的哪个版本完全依赖于运行时候
绑定到item的实参(动态)类型。
要注意: 动态绑定只有当我们通过指针或者引用调用虚函数时才会发生。