1.3 对象的差异(An Object Distinction)(2)
你绝对没有办法确定地说出px或rx到底指向何种类型的objects,你只能够说它要么是一个Library_materials object,要么就是后者的一个子类型(subtype)。不过,我们倒是可以确定,dx只能是Library_materials class的一个object。本节稍后,我会讨论为什么这样的行为虽然或许未如你所预期,却是良好的行为。
虽然"对于object的多态操作"要求此object必须可以经由一个pointer或reference来存取,然而C++(www.cppentry.com)中的pointer或reference的处理却不是多态的必要结果。想想下面的情况:
- // 没有多态(译注:因为操作对象不是 class object)
- int *pi;
-
- // 没有语言所支持的多态(译注:因为操作对象不是 class object)
- void *pvi;
-
- // ok : class x 视为一个 base class(译注:可以有多态的效果)
- x *px;
在C++(www.cppentry.com),多态只存在于一个个的public class体系中。举个例子,px可能指向某个类型的object,或指向根据public继承关系派生而来的一个子类型(请不要把不良的转换操作考虑在内)。Nonpublic的派生行为以及类型为void*的指针可以说是多态的,但它们并没有被语言明确地支持,也就是说它们必须由程序员通过显式的转换操作来管理(你或许可以说它们并不是多态对象的一线选手)。
C++(www.cppentry.com)以下列方法支持多态:
1.经由一组隐式的转化操作。例如把一个derived class指针转化为一个指向其public base type的指针:
- shape *ps = new circle();
2.经由virtual function机制:- ps->rotate();
3.经由dynamic_cast和typeid运算符:- if ( circle *pc = dynamic_cast< circle* >( ps ) ) ...
多态的主要用途是经由一个共同的接口来影响类型的封装,这个接口通常被定义在一个抽象的base class中。例如Library_materials class就为Book、Video、Puppet等subtype定义了一个接口。这个共享接口是以virtual function机制引发的,它可以在执行期根据object的真正类型解析出到底是哪一个函数实例被调用。经由这样的操作: - Library_material->check_out();
我们的代码可以避免由于"借助某一特定library的materials"而导致变动无常。这不只使得"当类型有所增加、修改或删减时,我们的程序代码无须改变",而且也使一个新的Library_materials subtype的供应者不需要重新写出"对继承体系中的所有类型都共通"的行为和操作。
考虑以下这样的代码:
- void rotate(
- X datum,
- const X *pointer,
- const X &reference )
- {
- // 在执行期之前,无法决定到底调用哪一个 rotate() 实例
- (*pointer).rotate();
- reference.rotate();
-
- // 下面这个操作总是调用 X::rotate()
- datum.rotate();
- }
-
- main() {
- Z z; // Z 是 X 的一个子类型
-
- rotate( z, &z, z );
- return 0;
- }
经由pointer和reference完成的两个"函数调用操作"会被动态完成!此例中它们都调用Z::rotate()。经由datum完成的"函数调用操作"则可能(或可能不)经由virtual机制。不过,它反正总是调用X::rotate()就是了(这就是所谓的"编译素养"问题:不管经由datum所调用的virtual function采不采用virtual机制,从语意来说,结果都是相同的。4.2节对此有更详细的讨论)。
需要多少内存才能够表现一个class object?一般而言要有:
其nonstatic data members的总和大小。
加上任何由于alignment(译注)的需求而填补(padding)上去的空间(可能存在于members之间,也可能存在于集合体边界)。
译注:alignment就是将数值调整到某数的倍数。在32位计算机上,通常alignment为4 bytes(32位), 以使bus的"运输量"达到最高效率。
加上为了支持virtual而由内部产生的任何额外负担(overhead)。
一个指针 ,不管它指向哪一种数据类型,指针本身所需的内存大小是固定的。举个例子,下面有一个ZooAnimal声明:
- class ZooAnimal {
- public:
- ZooAnimal();
- virtual ~ZooAnimal();
-
- // ...
- virtual void rotate();
-
- protected:
- int loc;
- String name;
- };
-
- ZooAnimal za( "Zoey" );
- ZooAnimal *pza = &za;