1.3 对象的差异(An Object Distinction)(3)
其中的class object za和指针pza的可能布局如图1.4所示。我将在第3章再回到"data members的布局"这个主题上。
指针的类型(The Type of a Pointer)
但是,一个指向ZooAnimal的指针是如何地与一个指向整数的指针或一个指向template Array(如下,与一个String一并产生)的指针有所不同呢?
- ZooAnimal *px;
- int *pi;
- Array< String > *pta;
以内存需求的观点来说,没有什么不同!它们三个都需要有足够的内存来放置一个机器地址(通常是个word,译注)。"指向不同类型之各指针"间的差异,既不在其指针表示法不同,也不在其内容(代表一个地址)不同,而是在其所寻址出来的object类型不同。也就是说,"指针类型"会教导编译器如何解释某个特定地址中的内存内容及其大小:
译注:Lippman视"不同机器上的word为可变大小,int则固定是16-bits",但另有一种说法是,"不同机器上的int为可变大小,word固定为16-bits",不可不察!
一个指向地址1000的整数指针,在32位机器上,将涵盖地址空间1000~1003(译注:因为32位机器上的整数是4-bytes)。
如果String是传统的8-bytes(包括一个4-bytes的字符指针和一个用来表示字符串长度的整数),那么一个ZooAnimal指针将横跨地址空间1000~1015(译注:4+8+4,如图1.4所示)。
|
| 图1.4 独立(非派生)class的object布局和pointer布局 |
嗯,那么,一个指向地址1000而类型为void*的指针,将涵盖怎样的地址空间呢?是的,我们不知道!这就是为什么一个类型为void*的指针只能够持有一个地址,而不能够通过它操作所指之object的缘故。
所以,转换(cast)其实是一种编译器指令。大部分情况下它并不改变一个指针所含的真正地址,它只影响"被指出之内存的大小和其内容"的解释方式。
加上多态之后(Adding Polymorphism)
现在,让我们定义一个Bear,作为一种ZooAnimal。当然,经由"public继承"可以完成这项任务:
- class Bear : public ZooAnimal {
- public:
- Bear();
- ~Bear();
- // ...
- void rotate();
- virtual void dance();
- // ...
- protected:
- enum Dances { ... };
-
- Dances dances_known;
- int cell_block;
- };
-
- Bear b( "Yogi" );
- Bear *pb = &b;
- Bear &rb = *pb;
b、pb、rb会有怎样的内存需求呢?不管是pointer或reference都只需要一个word的空间(在32位机器上是4-bytes)。Bear object需要24 bytes,也就是ZooAnimal的16 bytes加上Bear所带来的8 bytes。图1.5展示了可能的内存布局。
|
| 图1.5 Derived class的object和pointer布局 |
好,假设我们的Bear object放在地址1000处,一个Bear指针和一个ZooAnimal指针有什么不同?
- Bear b;
- ZooAnimal *pz = &b;
- Bear *pb = &b;
它们每个都指向Bear object的第一个byte。其间的差别是,pb所涵盖的地址包含整个Bear object,而pz所涵盖的地址只包含Bear object中的ZooAnimal subobject。
除了ZooAnimal subobject中出现的members,你不能够使用pz来直接处理Bear的任何members。唯一例外是通过virtual机制:
- // 不合法:cell_block不是ZooAnimal的一个member,
- // 虽然我们知道pz目前指向一个 Bear object。
- pz->cell_block;
-
- // ok: 经过一个显式的downcast操作就没有问题!
- (static_cast< Bear* >( pz ))->cell_block;
-
- // 下面这样更好,但它是一个run-time operation(译注:成本较高)
- if ( Bear* pb2 = dynamic_cast< Bear* >( pz ))
- pb2->cell_block;
-
- // ok: 因为cell_block是Bear的一个member。
- pb->cell_block;