- 纯虚函数
在前面所提到过的 Disc_item 类提出了一个有趣的问题:该类从 Item_base 继承了 net_price 函数但没有重定义该函数。因为对 Disc_item 类而言没有可以给予该函数的意义,所以没有重定义该函数。在我们的应用程序中,Disc_item 不对应任何折扣策略,这个类的存在只是为了让其他类继承。//也就是说,Disc_item继承了Item_base中的虚函数net_price,但是由于Disc_item不需要net_price,所以它没有重定义net_price
我们不想让用户定义 Disc_item 对象,相反,Disc_item 对象只应该作为 Disc_item 派生类型的对象的一部分而存在。但是,正如已定义的,没有办法防止用户定义一个普通的 Disc_item 对象。这带来一个问题:如果用户创建一个 Disc_item 对象并调用该对象的 net_price 函数,会发生什么呢?从前面章节的讨论中了解到,结果将是调用从 Item_base 继承而来的 net_price 函数,该函数产生的是不打折的价格。很难说用户可能期望调用 Disc_item 的 net_price 会有什么样的行为。真正的问题在于,我们宁愿用户根本不能创建这样的对象。可以使 net_price 成为纯虚函数,强制实现这一设计意图并正确指出 Disc_item 的 net_price 版本没有意义的。在函数形参表后面写上 = 0 以指定纯虚函数:
class Disc_item : public Item_base { public: double net_price(std::size_t) const = 0; };
将函数定义为纯虚能够说明,该函数为后代类型提供了可以覆盖的接口,但是这个类中的版本决不会调用。重要的是,用户将不能创建 Disc_item 类型的对象。
试图创建抽象基类的对象将发生编译时错误:
// Disc_item declares pure virtual functions Disc_item discounted; // error: can‘t define a Disc_item object Bulk_item bulk; // ok: Disc_item subobject within Bulk_item
含有(或继承)一个或多个纯虚函数的类是抽象基类。除了作为抽象基类的派生类的对象的组成部分,不能创建抽象类型的对象。
- 容器与继承
我们希望使用容器(或内置数组)保存因继承而相关联的对象。但是,对象不是多态的,这一事实对将容器用于继承层次中的类型有影响。例如,书店应用程序中可能有购物篮,购物篮代表顾客正在购买的书。我们希望能够在 multiset中存储储购买物,要定义 multiset,必须指定容器将保存的对象的类型。将对象放进容器时,复制元素。
如果定义 multiset 保存基类类型的对象:
multiset<Item_base> basket; Item_base base; Bulk_item bulk; basket.insert(base); // ok: add copy of base to basket basket.insert(bulk); // ok: but bulk sliced down to its base part
则加入派生类型的对象时,只将对象的基类部分保存在容器中。记住,将派生类对象复制到基类对象时,派生类对象将被切掉。
容器中的元素是 Item_base 对象,无论元素是否作为 Bulk_item 对象的副本而建立,当计算元素的 net_price 时,元素将按不打折定价。一旦对象放入了 multiset,它就不再是派生类对象了。因为派生类对象在赋值给基类对象时会被“切掉”,所以容器与通过继承相关的类型不能很好地融合。
不能通过定义容器保存派生类对象来解决这个问题。在这种情况下,不能将 Item_base 对象放入容器——没有从基类类型到派生类型的标准转换。可以显式地将基类对象强制转换为派生类对象并将结果对象加入容器,但是,如果这样做,当试图使用这样的元素时,会产生大问题:在这种情况下,元素可以当作派生类对象对待,但派生类部分的成员将是未初始化的。
唯一可行的选择可能是使用容器保存对象的指针。这个策略可行,但代价是需要用户面对管理对象和指针的问题,用户必须保证只要容器存在,被指向的对象就存在。如果对象是动态分配的,用户必须保证在容器消失时适当地释放对象。
- 句柄类与继承