有的时候我们希望管理许多的对象,但我们不希望看到这一大堆的对象都以公开的方式在函数调用中传递。这种情况下,一个典型的方法是使用容器-实体关联。即我们抽象一个“容器类”,这个容器类可以关联着许多的实体,而我们在传递的过程中只需要传递这个容器的指针,不需要传递这许多的对象。例如下面的代码:
```c
typedef void* Item;
struct Package {
int count;
Item* items;
}
struct Package* NewPackage();
struct Package* AddItem(struct Package* p, Item* t);
int GetItemCount(struct Package* p);
Item* FunctionGetItem(struct Package* p);
```
这样,我们就可以把对 Item 的管理转移到对 Package 的管理。这种方法在今天已经比较常见,而且在C++中这都已经提出来作为了语言标准库的一部分,并根据容器-实体关联实现了许多不同的容器,他们支持的方法可能不同。
容器-实体关联很大程度上是单向的,管理容器可以实现对实体的管理,但反过来则不成立:不存在一个函数,从Item类查出Package类。这会需要这样子的接口:
```c
struct Package* GetPackageWhereItemIsIn(void* Item);
```
要实现这样子的函数并非不可能,实际上一些场合正是使用了这类的设计。但STL并不包括这样子的风格的函数。这里的关键不在于Item更大还是Package更大,而在于他们的地位不对等:通过Package可以查出Item,通过Item却查不出Package。这就不是交叉互指性。这是单向可指性。
单向可指性还有一个一对一的版本。这似乎有点不好理解,为什么不直接管理另一个对象,而是需要通过一个对象才能管理另外一个对象?但实际上单向可指性有许多优点,例如,他可以实现职能的解耦,他可以方便地增加一个临时的功能层。典型的单向可指性的使用例如为一个对象设计他的访问器对象:
```c
typedef void* Item
struct ItemAccesser {
int flag;
Item* Item;
}
```
在这里,可以通过 ItemAccesser 管理 Item ,并且 ItemAccesser 还可以有自己的属性。同样的,我们从 Item 并不能反查 ItemAccesser 。这其实也是一种常用的设计,在一些闭源的程序库,Item 类可能就是闭源的,但 ItemAccesser 有一定的头文件,如此提供了一种程序员可以在一定的范围和职责下管理和使用 Item 的方法。
交叉互指性则是两个类可以互相查出。他的典型出现如下:
```c
struct A;
struct B {
struct A* relativeA;
...
}
struct A {
struct B* relativeB;
}
```
这里需要使用 incomplete type 。如此,既可以通过A,管理B,又可以通过B,管理A。这就是交叉互指性。
当然,这不意味着交叉互指性可以滥用,是否需要交叉互指性应该根据实际情况考虑。
这似乎有点不可思议,为什么需要通过A,管理B,又可以通过B,管理A呢?实际上这种情况还是很常见的。例如游戏引擎里的代码:
```c
struct Actor;
struct World {
struct Actor* someActor;
...
}
struct Actor {
struct World* someWorld;
}
```
这样,通过Actor类可以反查这个Actor在哪个World,通过World,又可以查出有哪些Actor在这个World。
交叉互指性可以用来分析“设计模式”的本质,但我们暂时说到这里。
--
修改:darkk FROM 123.112.17.*
FROM 123.112.17.*