返回介绍

7.6 函数和结构

发布于 2024-10-08 23:14:05 字数 7043 浏览 0 评论 0 收藏 0

现在将注意力从数组转到结构。为结构编写函数比为数组编写函数要简单得多。虽然结构变量和数组一样,都可以存储多个数据项,但在涉及到函数时,结构变量的行为更接近于基本的单值变量。也就是说,与数组不同,结构将其数据组合成单个实体或数据对象,该实体被视为一个整体。前面讲过,可以将一个结构赋给另外一个结构。同样,也可以按值传递结构,就像普通变量那样。在这种情况下,函数将使用原始结构的副本。另外,函数也可以返回结构。与数组名就是数组第一个元素的地址不同的是,结构名只是结构的名称,要获得结构的地址,必须使用地址运算符&。在 C 语言和 C++中,都使用符号&来表示地址运算符;另外,C++还使用该运算符来表示引用变量,这将在第 8 章讨论。

使用结构编程时,最直接的方式是像处理基本类型那样来处理结构;也就是说,将结构作为参数传递,并在需要时将结构用作返回值使用。然而,按值传递结构有一个缺点。如果结构非常大,则复制结构将增加内存要求,降低系统运行的速度。出于这些原因(同时由于最初 C 语言不允许按值传递结构),许多 C 程序员倾向于传递结构的地址,然后使用指针来访问结构的内容。C++提供了第三种选择——按引用传递(将在第 8 章介绍)。下面介绍其他两种传递方式,首先介绍传递和返回整个结构。

7.6.1 传递和返回结构

当结构比较小时,按值传递结构最合理,下面来看两个使用这种技术的示例。第一个例子处理行程时间。有些地图指出,从 Thunder Falls 到 Bingo 城需要 3 小时 50 分钟,而从 Bingo 城到 Gotesquo 需要 1 小时 25 分钟。对于这种时间,可以使用结构来表示——一个成员表示小时值,另一个成员表示分钟值。将两个时间加起来需要一些技巧,因为可能需要将分钟值转换为小时。例如,前面列出的两个时间的总和为 4 小时 75 分钟,应将它转换为 5 小时 15 分钟。下面开发用于表示时间值的结构,然后再开发一个函数,它接受两个这样的结构为参数,并返回表示参数的和的结构。

定义结构的工作很简单:

接下来,看一下返回两个这种结构的总和的 sum( ) 函数的原型。返回值的类型应为 travel_time,两个参数也应为这种类型。因此,原型应如下所示:

要将两个时间相加,应首先将分钟成员相加。然后通过整数除法(除数为 60)得到小时值,通过求模运算符(%)得到剩余的分钟数。程序清单 7.11 在 sum( ) 函数中使用了这种计算方式,并使用 show_time( ) 函数显示 travel_time 结构的内容。

程序清单 7.11 travel.cpp

其中,travel_time 就像是一个标准的类型名,可被用来声明变量、函数的返回类型和函数的参数类型。由于 total 和 t1 变量是 travel_time 结构,因此可以对它们使用句点成员运算符。由于 sum( ) 函数返回 travel_time 结构,因此可以将其用作 show_time( ) 函数的参数。由于在默认情况下,C++函数按值传递参数,因此函数调用 show_time(sum(trip, day3)) 将执行函数调用 sum(trip, day3),以获得其返回值。然后,show_time( ) 调用将 sum( ) 的返回值(而不是函数自身)传递给 show_time( )。下面是该程序的输出:

7.6.2 另一个处理结构的函数示例

前面介绍的有关函数和 C++结构的大部分知识都可用于 C++类中,因此有必要介绍另一个示例。这次要处理的是空间,而不是时间。具体地说,这个例子将定义两个结构,用于表示两种不同的描述位置的方法,然后开发一个函数,将一种格式转换为另一种格式,并显示结果。这个例子用到的数学知识比前一个要多,但并不需要像学习数学那样学习 C++。

假设要描述屏幕上某点的位置,或地图上某点相对于原点的位置,则一种方法是指出该点相对于原点的水平偏移量和垂直偏移量。传统上,数学家使用 x 表示水平偏移量,使用 y 表示垂直偏移量(参见图 7.6)。x 和 y 一起构成了直角坐标(rectangular coordinates)。可以定义由两个坐标组成的结构来表示位置:

图 7.6 直角坐标

另一种描述点的位置的方法是,指出它偏离原点的距离和方向(例如,东偏北 40 度)。传统上,数学家从正水平轴开始按逆时针方向度量角度(参见图 7.7)。距离和角度一起构成了极坐标(polar coordinates)。可以定义另一个结构来表示这种位置:

图 7.7 极坐标

下面来创建一个显示 polar 结构的内容的函数。C++库(从 C 语言借鉴而来)中的数学函数假设角度的单位为弧度,因此应以弧度为单位来测量角度。但为了便于显示,我们将弧度值转换为角度值。这意味着需要将弧度值乘以 180/——约为 57.29577951。该函数的代码如下:

请注意,形参的类型为 polar。将一个 polar 结构传递给该函数时,该结构的内容将被复制到 dapos 结构中,函数随后将使用该拷贝完成工作。由于 dapos 是一个结构,因此该函数使用成员运算符句点(参见第 4 章)来标识结构成员。

接下来,让我们试着再前进一步,编写一个将直角坐标转换为极坐标的函数。该函数接受一个 rect 参数,并返回一个 polar 结构。这需要使用数学库中的函数,因此程序必须包含头文件 cmath(在较旧的系统中为 math.h)。另外,在有些系统中,还必须命令编译器载入数学库(参见第 1 章)。可以根据毕达哥拉斯定理,使用水平和垂直坐标来计算距离:

数学库中的 atan2( ) 函数可根据 x 和 y 的值计算角度:

还有一个 atan( ) 函数,但它不能区分 180 度之内和之外的角度。在数学函数中,这种不确定性与在生存手册中一样不受人欢迎。

有了这些公式后,便可以这样编写该函数:

编写好函数后,程序的其他部分编写起来就非常简单了。程序清单 7.12 列出了程序的代码。

程序清单 7.12 atrctfun.cpp

注意:

有些编译器仅当被明确指示后,才会搜索数学库。例如,较早的 g++版本使用下面这样的命令行:

下面是该程序的运行情况:

程序说明

程序清单 7.12 中的两个函数已经在前面讨论了,因此下面复习一下该程序如何使用 cin 来控制 while 循环:

前面讲过,cin 是 istream 类的一个对象。抽取运算符(>>)被设计成使得 cin>>rplace.x 也是一个 istream 对象。正如第 11 章将介绍的,类运算符是使用函数实现的。使用 cin>>rplace.x 时,程序将调用一个函数,该函数返回一个 istream 值。将抽取运算符用于 cin>>rplace.x 对象(就像 cin>>rplace.x>>rplace.y 这样),也将获得一个 istream 对象。因此,整个 while 循环的测试表达式的最终结果为 cin,而 cin 被用于测试表达式中时,将根据输入是否成功,被转换为 bool 值 true 或 false。例如,在程序清单 7.12 中的循环中,cin 期望用户输入两个数字,如果用户输入了 q(前面的输出示例就是这样做的),cin>>将知道 q 不是数字,从而将 q 留在输入队列中,并返回一个将被转换为 fasle 的值,导致循环结束。

请将这种读取数字的方法与下面更为简单的方法进行比较:

要提早结束该循环,可以输入一个负值。这将输入限制为非负值。这种限制符合某些程序的需要,但通常需要一种不会将某些数值排除在外的、终止循环的方式。将 cin>>用作测试条件消除了这种限制,因为它接受任何有效的数字输入。在需要使用循环来输入数字时,别忘了考虑使用这种方式。另外请记住,非数字输入将设置一个错误条件,禁止进一步读取输入。如果程序在输入循环后还需要进行输入,则必须使用 cin.clear( ) 重置输入,然后还可能需要通过读取不合法的输入来丢弃它们。程序清单 7.7 演示了这些技术。

7.6.3 传递结构的地址

假设要传递结构的地址而不是整个结构以节省时间和空间,则需要重新编写前面的函数,使用指向结构的指针。首先来看一看如何重新编写 show_polar( ) 函数。需要修改三个地方:

  • 调用函数时,将结构的地址(&pplace)而不是结构本身(pplace)传递给它;
  • 将形参声明为指向 polar 的指针,即 polar *类型。由于函数不应该修改结构,因此使用了 const 修饰符;
  • 由于形参是指针而不是结构,因此应间接成员运算符(->),而不是成员运算符(句点)。

完成上述修改后,该函数如下所示:

接下来对 rect_to_polar 进行修改。由于原来的 rect_to_polar 函数返回一个结构,因此修改工作更复杂些。为了充分利用指针的效率,应使用指针,而不是返回值。为此,需要将两个指针传递给该函数。第一个指针指向要转换的结构,第二个指针指向存储转换结果的结构。函数不返回一个新的结构,而是修改调用函数中已有的结构。因此,虽然第一个参数是 const 指针,但第二个参数却不是。也可以像修改函数 show_polar( ) 修改这个函数。程序清单 7.13 列出了修改后的程序。

程序清单 7.13 strctptr.cpp

注意:

有些编译器需要明确指示,才会搜索数学库。例如,较早的 g++版本使用下面这样的命令行:

从用户的角度来说,程序清单 7.13 的行为与程序清单 7.12 相同。它们之间的差别在于,程序清单 7.12 使用的是结构副本,而程序清单 7.13 使用的是指针,让函数能够对原始结构进行操作。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文