4.8 嵌套结构

在全局名字空间之外为数据和函数取名字的好处可以扩展到结构中。我们可以将一个结构嵌套在另一个结构中,这就可以将相关联的元素放在一起。声明文法是我们所期望的形式,就像在下面结构中可以看到的那样,这个结构用简单链表方式实现了一个下推栈(push-down stack),所以它绝不会越出内存。

4.8 嵌套结构 - 图1

这个嵌套struct称为Link,它包括一个指向这个表中的下一个Link的指针和一个指向存放在Link中的数据的指针。如果next指针是零,这就意味着到了表尾。

注意:head指针紧接在struct Link声明之后定义,而不是单独定义Link*head。这是来自C语言的一种文法,但它强调在结构声明之后的分号的重要性,分号表明这个结构类型用逗号分开的定义表结束(通常这个定义表是空的)。

正如到目前为止所有描述的结构一样,嵌套结构有它自己的initialize()函数,以便确保正确的初始化。Stack既有initialize()函数又有cleanup()函数,此外还有push()函数,它取一个指向希望存放的数据(假设已经分配在堆中)的指针;还有pop()函数,它返回栈顶的data指针并去除栈顶元素。(注意,当pop()一个元素时,我们有责任销毁由data所指的对象)。peek()函数也从栈顶返回data指针,但是它在栈(Stack)中保留这个栈顶元素。

下面是一些成员函数的定义:

4.8 嵌套结构 - 图2

4.8 嵌套结构 - 图3

第一个定义特别有趣,因为它表明如何去定义一个嵌套结构的成员。只需要使用一个额外的作用域解析层,说明外围struct的名字。Stack:Link:initialize()函数取参数并把参数赋给它的成员们。

Stack:initialize()函数置head为零,使得这个对象知道它有一个空表。

Stack:push()取参数,也就是一个指向希望用的变量的指针,并且把这个指针推入Stack。首先,使用new为Link分配空间,它将插入栈顶。然后调用Link的initialize()函数对这个Link的成员赋相应的值。注意,给next指针赋当前的head,而给head赋新的Link指针。这就有效地将Link推向这个表的顶部了。

Stack:pop()取出当前在该Stack顶部的data指针,然后向下移head指针,删除该Stack的旧的栈顶元素,最后返回这个取出的指针。当pop()取出了最后的元素后,head再次变为零,意味着Stack为空。

实际上,Stack:cleanup()不做任何清除工作,而是确立一项硬性的策略,“你(即使用这个Stack对象的客户程序员)负责弹出这个Stack的所有元素并且删除它们。”require()指出:如果Stack非空,就产生一个编程错误。

为什么Stack的析构函数不能对客户程序员不做pop()的所有对象负责呢?问题是,Stack存放的是void指针,而且在第13章中我们将了解对void*调用delete不能正确地清除内容。“谁对内存负责”这个主题不是一个简单的问题,在后面章节中将会看到相关的内容。

下面是一个测试Stack的例子:

4.8 嵌套结构 - 图4

4.8 嵌套结构 - 图5

这个例子非常类似于前面一个例子,但是它把来自文件的行(作为string指针)存放到Stack中,然后弹出它们,这会使这个文件被逆序打印出来。注意pop()成员函数返回一个void,并且必须在被用之前转换回string。间接引用指针以便打印string。

当填充textlines时,通过建立new string(line)为每个Push()“复制”line的内容。从新表达式返回的值是指向这个新创建的string的指针,并且从line复制信息。如果简单地传递line地址给push(),最终用相同的地址充填Stack,所有的指针都指向line。在本书的后面,我们将学习更多的“复制”过程。

文件名取自命令行。为了保证在命令行上有足够的参数,我们看require.h头文件中的第二个函数requireArgs(),它比较argc与期望的参数个数,如果没有足够的参数,打印相应的错误信息并退出程序。

4.8.1 全局作用域解析

编译器默认选择的名字(“最接近”的名字)可能不是我们要用的名字,作用域解析运算符可以避免这种情况。例如,假设有一个结构,它的局域标识符为a,但是我们希望在成员函数内选用全局标识符a。这时,编译器将默认选择局域的另一个标识符,因而必须告诉编译器应该选择哪个标识符。当你要用作用域解析运算符指定一个全局名字时,在运算符前面不加任何东西。下面是一个显示变量和函数的全局作用域解析的例子:

4.8 嵌套结构 - 图6

4.8 嵌套结构 - 图7

如果在S:f()中没有作用域解析运算符,编译器会默认地选择成员函数的f()和a。