Team LiB
Previous Section Next Section

15.1. OOP: An Overview

15.1. 面向对象编程:概述

The key idea behind OOP is polymorphism. Polymorphism is derived from a Greek word meaning "many forms." We speak of types related by inheritance as polymorphic types, because in many cases we can use the "many forms" of a derived or base type interchangeably. As we'll see, in C++, polymorphism applies only to references or pointers to types related by inheritance.

面向对象编程的关键思想是多态性(polymorphism)。多态性派生于一个希腊单词,意思是“许多形态”。之所以称通过继承而相关联的类型为多态类型,是因为在许多情况下可以互换地使用派生类型或基类型的“许多形态”。正如我们将看到的,在 C++ 中,多态性仅用于通过继承而相关联的类型的引用或指针。

Inheritance

继承

Inheritance lets us define classes that model relationships among types, sharing what is common and specializing only that which is inherently different. Members defined by the base class are inherited by its derived classes. The derived class can use, without change, those operations that do not depend on the specifics of the derived type. It can redefine those member functions that do depend on its type, specializing the function to take into account the peculiarities of the derived type. Finally, a derived class may define additional members beyond those it inherits from its base class.

通过继承我们能够定义这样的类,它们对类型之间的关系建模,共享公共的东西,仅仅特化本质上不同的东西。派生类(derived class)能够继承基类(base class)定义的成员,派生类可以无须改变而使用那些与派生类型具体特性不相关的操作,派生类可以重定义那些与派生类型相关的成员函数,将函数特化,考虑派生类型的特性。最后,除了从基类继承的成员之外,派生类还可以定义更多的成员。

Classes related by inheritance are often described as forming an inheritance hierarchy. There is one class, referred to as the root, from which all the other classes inherit, directly or indirectly. In our bookstore example, we will define a base class, which we'll name Item_base, to represent undiscounted books. From Item_base we will inherit a second class, which we'll name Bulk_item, to represent books sold with a quantity discount.

我们经常称因继承而相关联的类为构成了一个继承层次。其中有一个类称为根,所以其他类直接或间接继承根类。在书店例子中,我们将定义一个基类,命名为 Item_base,命名为 Bulk_item,表示带数量折扣销售的书。

At a minimum, these classes will define the following operations:

这些类至少定义如下操作:

  • an operation named book that will return the ISBN

    名为 book 的操作,返回 ISBN。

  • an operation named net_price that returns the price for purchasing a specified number of copies of a book

    名为 net_price 的操作,返回购买指定数量的书的价格。

Classes derived from Item_base will inherit the book function without change: The derived classes have no need to redefine what it means to fetch the ISBN. On the other hand, each derived class will need to define its own version of the net_price function to implement an appropriate discount pricing strategy.

Item_base 的派生类将无须改变地继承 book 函数:派生类不需要重新定义获取 ISBN 的含义。另一方面,每个派生类需要定义自己的 net_price 函数版本,以实现适当的折扣价格策略。

In C++, a base class must indicate which of its functions it intends for its derived classes to redefine. Functions defined as virtual are ones that the base expects its derived classes to redefine. Functions that the base class intends its children to inherit are not defined as virtual.

在 C++ 中,基类必须指出希望派生类重写哪些函数,定义为 virtual 的函数是基类期待派生类重新定义的,基类希望派生类继承的函数不能定义为虚函数。

Given this discussion, we can see that our classes will define three (const) member functions:

讨论过这些之后,可以看到我们的类将定义三个(const)成员函数:

  • A nonvirtual function, std::string book(), that returns the ISBN. It will be defined by Item_base and inherited by Bulk_item.

    非虚函数 std::string book(),返回 ISBN。由 Item_base 定义,Bulk_item 继承。

  • Two versions of the virtual function, double net_price(size_t), to return the total price for a given number of copies of a specific book. Both Item_base and Bulk_item will define their own versions of this function.

    虚函数 double net_price(size_t) 的两个版本,返回给定数目的某书的总价。Item_base 类和 Bulk_item 类将定义该函数自己的版本。

Dynamic Binding

动态绑定

Dynamic binding lets us write programs that use objects of any type in an inheritance hierarchy without caring about the objects' specific types. Programs that use these classes need not distinguish between functions defined in the base or in a derived class.

动态绑定我们能够编写程序使用继承层次中任意类型的对象,无须关心对象的具体类型。使用这些类的程序无须区分函数是在基类还是在派生类中定义的。

For example, our bookstore application would let a customer select several books in a single sale. When the customer was done shopping, the application would calculate the total due. One part of figuring the final bill would be to print for each book purchased a line reporting the total quantity and sales price for that portion of the purchase.

例如,书店应用程序可以允许顾客在一次交易中选择几本书,当顾客购书时,应用程序可以计算总的应付款,指出最终账单的一个部分将是为每本书打印一行,以显示总数和售价。

We might define a function named print_total to manage this part of the application. The print_total function, given an item and a count, should print the ISBN and the total price for purchasing the given number of copies of that particular book. The output of this function should look like:

可以定义一个名为 print_total 的函数管理应用程序的这个部分。给定一个项目和数量,函数应打印 ISBN 以及购买给定数量的某书的总价。这个函数的输出应该像这样:

     ISBN: 0-201-54848-8 number sold: 3 total price: 98
     ISBN: 0-201-82470-1 number sold: 5 total price: 202.5

Our print_total function might look something like the following:

可以这样编写 print_total 函数:

     // calculate and print price for given number of copies, applying any discounts
     void print_total(ostream &os,
                      const Item_base &item, size_t n)
     {
          os << "ISBN: " << item.book() // calls Item_base::book
             << "\tnumber sold: " << n << "\ttotal price: "
             // virtual call: which version of net_price to call is resolved at run time
             << item.net_price(n) << endl;
     }

The function's work is trivial: It prints the results of calling book and net_price on its item parameter. There are two interesting things about this function.

该函数的工作很普通:调用其 item 形参的 booknet_price 函数,打印结果。关于这个函数,有两点值得注意。

First, even though its second parameter is a reference to Item_base, we can pass either an Item_base object or a Bulk_item object to this function.

第一,虽然这个函数的第二形参是 Item_base 的引用但可以将 Item_base 对象或 Bulk_item 对象传给它。

Second, because the parameter is a reference and the net_price function is virtual, the call to net_price will be resolved at run time. The version of net_price that is called will depend on the type of the argument passed to print_total. When the argument to print_total is a Bulk_item, the version of net_price that is run will be the one defined in Bulk_item that applies a discount. If the argument is an Item_base object, then the call will be to the version defined by Item_base.

第二,因为形参是引用且 net_price 是虚函数,所以对 net_price 的调用将在运行时确定。调用哪个版本的 net_price 将依赖于传给 print_total 的实参。如果传给 print_total 的实参是一个 Bulk_item 对象,将运行 Bulk_item 中定义的应用折扣的 net_price;如果实参是一个 Item_base 对象,则调用由 Item_base 定义的版本。

In C++, dynamic binding happens when a virtual function is called through a reference (or a pointer) to a base class. The fact that a reference (or pointer) might refer to either a base- or a derived-class object is the key to dynamic binding. Calls to virtual functions made through a reference (or pointer) are resolved at run time: The function that is called is the one defined by the actual type of the object to which the reference (or pointer) refers.

在 C++ 中,通过基类的引用(或指针)调用虚函数时,发生动态绑定。引用(或指针)既可以指向基类对象也可以指向派生类对象,这一事实是动态绑定的关键。用引用(或指针)调用的虚函数在运行时确定,被调用的函数是引用(或指针)所指对象的实际类型所定义的。



Team LiB
Previous Section Next Section