第15章 面向对象程序设计
OOP:概述(OOP:An Overview)
面向对象程序设计(object-oriented programming)的核心思想是数据抽象(封装)、继承和动态绑定(多态)。
通过继承(inheritance)联系在一起的类构成一种层次关系。通常在层次关系的根部有一个基类(base class),其他类则直接或间接地从基类继承而来,这些继承得到的类叫做派生类(derived class)。基类负责定义在层次关系中所有类共同拥有的成员,而每个派生类定义各自特有的成员。
对于某些函数,基类希望它的派生类各自定义适合自身的版本,此时基类应该将这些函数声明为虚函数(virtual function)。方法是在函数名称前添加virtual
关键字。
class Quote
{
public:
std::string isbn() const;
virtual double net_price(std::size_t n) const;
};
派生类必须通过类派生列表(class derivation list)明确指出它是从哪个或哪些基类继承而来的。类派生列表的形式首先是一个冒号,后面紧跟以逗号分隔的基类列表,其中每个基类前面可以添加访问说明符。
class Bulk_quote : public Quote
{
// Bulk_quote inherits from Quote
public:
double net_price(std::size_t) const override;
};
派生类必须在其内部对所有重新定义的虚函数进行声明。
使用基类的引用或指针调用一个虚函数时将发生动态绑定(dynamic binding),也叫运行时绑定(run-time binding)。函数的运行版本将由实参决定。
定义基类和派生类(Defining Base and Derived Classes)
定义基类(Defining a Base Class)
基类通常都应该定义一个虚析构函数,即使该函数不执行任何实际操作也是如此。
除构造函数之外的任何非静态函数都能定义为虚函数。virtual
关键字只能出现在类内部的声明语句之前而不能用于类外部的函数定义。如果基类把一个函数声明为虚函数,则该函数在派生类中隐式地也是虚函数。
成员函数如果没有被声明为虚函数,则其解析过程发生在编译阶段而非运行阶段。
派生类能访问基类的公有成员,不能访问私有成员。如果基类希望定义外部代码无法访问,但是派生类对象可以访问的成员,可以使用受保护的(protected)访问运算符进行说明。
定义派生类(Defining a Derived Class)
类派生列表中的访问说明符用于控制派生类从基类继承而来的成员是否对派生类的用户可见。
如果派生类没有覆盖其基类的某个虚函数,则该虚函数的行为类似于其他的普通函数,派生类会直接继承其在基类中的版本。
C++标准并没有明确规定派生类的对象在内存中如何分布,一个对象中继承自基类的部分和派生类自定义的部分不一定是连续存储的。
因为在派生类对象中含有与其基类对应的组成部分,所以能把派生类的对象当作基类对象来使用,也能将基类的指针或引用绑定到派生类对象中的基类部分上。这种转换通常称为派生类到基类的(derived-to-base)类型转换,编译器会隐式执行。
Quote item; // object of base type
Bulk_quote bulk; // object of derived type
Quote *p = &item; // p points to a Quote object
p = &bulk; // p points to the Quote part of bulk
Quote &r = bulk; // r bound to the Quote part of bulk
每个类控制它自己的成员初始化过程,派生类必须使用基类的构造函数来初始化它的基类部分。派生类的构造函数通过构造函数初始化列表来将实参传递给基类构造函数。
Bulk_quote(const std::string& book, double p,
std::size_t qty, double disc) :
Quote(book, p), min_qty(qty), discount(disc) {
}
除非特别指出,否则派生类对象的基类部分会像数据成员一样执行默认初始化。
派生类初始化时首先初始化基类部分,然后按照声明的顺序依次初始化派生类成员。
派生类可以访问基类的公有成员和受保护成员。
如果基类定义了一个静态成员,则在整个继承体系中只存在该成员的唯一定义。如果某静态成员是可访问的,则既能通过基类也能通过派生类使用它。
已经完整定义的类才能被用作基类。
class Base {
/* ... */ } ;
class D1: public Base {
/* ... */ };
class D2: public D1 {
/* ... */ };
Base
是D1
的直接基类(direct base),是D2
的间接基类(indirect base)。最终的派生类将包含它直接基类的子对象以及每个间接基类的子对象。
C++11中,在类名后面添加final
关键字可以禁止其他类继承它。
class NoDerived final {
/* */ }; // NoDerived can't be a base class
class Base {
/* */ };
// Last is final; we cannot inherit from Last
class Last final : Base {
/* */ }; // Last can't be a base class
class Bad : NoDerived {
/* */ }; // error: NoDerived is final
class Bad2 : Last {
/* */