August 2021
Intermediate to advanced
450 pages
9h 36m
Chinese
人们总是尝试一种不可行的面向对象编程和泛型编程的组合方式:试图将派生类对象的容器作为基类对象的容器使用。例如:
但为什么不行呢?毕竟你会说“我能够将一个Circle转换为一个Shape”。但实际上你不能这么做。你可以将一个Circle*转换为一个Shape*,或者将一个Circle&转换为一个Shape&,但我们已经有意地禁止了Shape对象之间的赋值,因此你不必担心将一个具有半径属性的Circle存入一个没有半径属性的Shape变量时将会发生什么(见19.2.4节)。假如我们允许这种赋值,将会发生什么呢?类对象将会发生“切片”现象,类似整数截断(见3.9.2节)。
因此,我们改为尝试使用指针:
这一次,系统仍然报错;为什么呢?看一看f()可能会做些什么:
显然,我们可以将一个Rectangle*指针存入一个vector<Shape*>中。但是,如果这一vector<Shape*>对象在别的地方被解释为一个vector<Circle*>时,可能会得到令人惊讶的糟糕结果。特别地,假设上述例子能够在编译器中成功编译,那么在vpc中存放Rectangle*指针会产生什么结果呢?继承是一种强大但微妙的机制,而模板并没有隐含地扩展它。存在几种用模板表达继承的方法,但这些内容不在本书的介绍范围之内。我们只需记住,对于任意模板C,“D是B”并不意味着“C<D>是C<B>”——并且应重视这一规则的价值,它能避免意外的类型违规。参见25.4.4节。 ...