news 2026/2/25 2:04:19

函数—C++的编程模块(函数和string 对象)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
函数—C++的编程模块(函数和string 对象)

函数和string 对象

虽然C-风格字符串和string 对象的用途几乎相同,但与数组相比,string 对象与结构的更相似。例如,
可以将一个结构赋给另一个结构,也可以将一个对象赋给另一个对象。可以将结构作为完整的实体传递给
函数,也可以将对象作为完整的实体进行传递。如果需要多个字符串,可以声明一个string 对象数组,而
不是二维char 数组。

程序清单7.14 提供了一个小型示例,它声明了一个string 对象数组,并将该数组传递给一个函数以显
示其内容。

#include <iostream> #include<string> using namespace std; const int SIZE = 5; void display(const string sa[], int n); int main() { string list[SIZE]; cout << "Enter your" << SIZE << "favorite astronomical sights:\n"; for (int i = 0; i < SIZE; i++) { cout << i + 1 << ":"; getline(cin, list[i]); } cout << "Your list:\n"; display(list, SIZE); return 0; } void display(const string sa[], int n) { for (int i = 0; i < n; i++) cout << i + 1 << ":" << sa[i] << endl; }

输出结果

Enter your5favorite astronomical sights: 1:Orion Nebula 2:M13 3:Saturn 4:Jupiter 5:Moon Your list: 1:Orion Nebula 2:M13 3:Saturn 4:Jupiter 5:Moon

对于该示例,需要指出的一点是,除函数getline( )外,该程序像对待内置类型(如int)一样对待string
对象。如果需要string 数组,只需使用通常的数组声明格式即可:

string list[SIZE];

这样,数组list 的每个元素都是一个string 对象,可以像下面这样使用它:

getline(cin,list[i]);

同样,形参sa 是一个指向string 对象的指针,因此sa[i]是一个string 对象,可以像下面这样使用它:

cout<<i+1<<":"<<sa[i]<<endl;

函数与array 对象

在C++中,类对象是基于结构的,因此结构编程方面的有些考虑因素也适用于类。例如,可按值将对
象传递给函数,在这种情况下,函数处理的是原始对象的副本。另外,也可传递指向对象的指针,这让函
数能够操作原始对象。下面来看一个使用C++11 模板类array 的例子。
假设您要使用一个array 对象来存储一年四个季度的开支:

std::array<double,4>expenses;

本书前面说过,要使用array 类,需要包含头文件array,而名称array 位于名称空间std 中。如果函数
来显示expenses 的内容,可按值传递expenses:

show(expenses);

但如果函数要修改对象expenses,则需将该对象的地址传递给函数(下一章将讨论另一种方法—使
用引用):

fill(&expenses);

这与程序清单7.13 处理结构时使用的方法相同。
如何声明这两个函数呢?expenses 的类型为array<double, 4>,因此必须在函数原型中指定这种类型:

void show(std::array<doule,4>da); void fill(std::array<double,4>*pa);

这些考虑因素是这个示例程序的核心。该程序还包含其他一些功能。首先,它用符号常量替换了4:

const int Seasons=4;

其次,它使用了一个const array 对象,该对象包含4 个string 对象,用于表示几个季度:

const std::array<std::string,Seasons>Snames={ "Spring","Summer","Fall","Winter" };

请注意,模板array 并非只能存储基本数据类型,它还可存储类对象。程序清单7.15 列出了该程序的
完整代码。

#include <iostream> #include<array> #include<string> const int Seansons = 4; const std::array<std::string, Seansons>Snames = { "Spring","Summer","Fall","Winter" }; void fill(std::array<double, Seansons>* pa); void show(std::array<double, Seansons>da); int main() { std::array<double, Seansons>expenses; fill(&expenses); show(expenses); return 0; } void fill(std::array<double, Seansons>* pa) { using namespace std; for (int i = 0; i < Seansons; i++) { cout << "Enter" << Snames[i] << "expenses:"; cin >> (*pa)[i]; } } void show(std::array<double, Seansons>da) { using namespace std; double total = 0.0; cout << "\nEXPENSES\n"; for (int i = 0; i < Seansons; i++) { cout << Snames[i] << ":$" << da[i] << endl; total += da[i]; } cout << "Total expenses:$" << total << endl; }
EnterSpringexpenses:212 EnterSummerexpenses:256 EnterFallexpenses:208 EnterWinterexpenses:244 EXPENSES Spring:$212 Summer:$256 Fall:$208 Winter:$244 Total expenses:$920

递归

下面介绍一些完全不同的内容。C++函数有一种有趣的特点—可以调用自己(然而,与C 语言不同
的是,C++不允许main( )调用自己),这种功能被称为递归。尽管递归在特定的编程(例如人工智能)中是
一种重要的工具,但这里只简单地介绍一下它是如何工作的。

包含一个递归调用的递归

如果递归函数调用自己,则被调用的函数也将调用自己,这将无限循环下去,除非代码中包含终止调
用链的内容。通常的方法将递归调用放在if 语句中。例如,void 类型的递归函数recurs( )的代码如下:

void recurs(argumentlist) { statements1 if(test) recurs(arguments) statements2 }

test 最终将为false,调用链将断开。
递归调用将导致一系列有趣的事件。只要if 语句为true,每个recurs( )调用都将执行statements 1,然
后再调用recurs( ),而不会执行statements 2。当if 语句为false 时,当前调用将执行statements2。当前调用
结束后,程序控制权将返回给调用它的recurs( ),而该recurs( )将执行其stataments2 部分,然后结束,并将
控制权返回给前一个调用,依此类推。因此,如果recurs( )进行了5 次递归调用,则第一个statements1 部
分将按函数调用的顺序执行5 次,然后statements2 部分将以与函数调用相反的顺序执行5 次。进入5 层递
归后,程序将沿进入的路径返回。程序清单7.16 演示了这种行为。

#include <iostream> void countdown(int n); int main() { countdown(4); return 0; } void countdown(int n) { using namespace std; cout << "Counting down ..." << n << endl; if(n>0) countdown(n-1); cout << n << ":Kaboom!\n"; }

输出结果

Counting down ...4 Counting down ...3 Counting down ...2 Counting down ...1 Counting down ...0 0:Kaboom! 1:Kaboom! 2:Kaboom! 3:Kaboom! 4:Kaboom!

注意,每个递归调用都创建自己的一套变量,因此当程序到达第5 次调用时,将有5 个独立的n 变量,其中每个变量的值都不同。为验证这一点,读者可以修改程序清单7.16,使之显示n 的地址和值:

cout<<"Counting down..."<<n<<"(n at "<<&n<<")"<<endl; ... cout<<n<<":kaboom!";<<" (n at "<<&n<<)"<<endl;

注意,在一个内存单元(内存地址为0012FE0C),存储的n 值为4;在另一个内存单元(内存地址为
0012FD34),存储的n 值为3;等等。另外,注意到在Counting down 阶段和Kaboom 阶段的相同层级,n 的
地址相同。

运行结果

Counting down ...4 Counting down ...3 Counting down ...2 Counting down ...1 Counting down ...0 0:Kaboom! 1:Kaboom! 2:Kaboom! 3:Kaboom! 4:Kaboom!

包含多个递归调用的递归

在需要将一项工作不断分为两项较小的、类似的工作时,递归非常有用。例如,请考虑使用这种方法
来绘制标尺的情况。标出两端,找到中点并将其标出。然后将同样的操作用于标尺的左半部分和右半部分。
如果要进一步细分,可将同样的操作用于当前的每一部分。递归方法有时被称为分而治之策略
(divide-and-conquer strategy)。程序清单7.17 使用递归函数subdivide( )演示了这种方法,该函数使用一个
字符串,该字符串除两端为 | 字符外,其他全部为空格。main 函数使用循环调用subdivide( )函数6 次,每
次将递归层编号加1,并打印得到的字符串。这样,每行输出表示一层递归。该程序使用限定符std::而不
是编译指令using,以提醒读者还可以采取这种方式。

#include <iostream> const int Len = 66; const int Divs = 6; void subdivide(char ar[], int low, int high, int level); int main() { char ruler[Len]; int i; for (i = 1; i < Len - 2; i++) { ruler[i] = ' '; } ruler[Len - 1] = '\0'; int max = Len - 2; int min = 0; ruler[min] = ruler[max] = '|'; std::cout << ruler << std::endl; for (i = 1; i <= Divs; i++) { subdivide(ruler, min, max, i); std::cout << ruler << std::endl; for (int j = 1; j < Len - 2; j++) ruler[j] = ' '; } return 0; } void subdivide(char ar[], int low, int high, int level) { if (level == 0) return; int mid = (high + low) / 2; ar[mid] = '|'; subdivide(ar, low, mid, level - 1); subdivide(ar, mid, high, level - 1); }

运行结果

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/23 1:40:56

把 SAP ABAP 的消息与异常处理做成标准件:从 MESSAGE 到 TRY ... CATCH 的工程化落地

在 SAP 系统里写程序,难点往往不在业务逻辑本身,而在失败时怎么失败:用户看到什么提示、后台作业怎么留痕、接口调用方如何拿到可处理的错误、以及出了问题能不能快速定位。消息与错误处理如果没有统一标准,结果通常是两类极端:要么满屏MESSAGE E...把用户“堵死”,要么关…

作者头像 李华
网站建设 2026/2/6 22:36:36

强软弱虚引用如何理解

强引用&#xff1a;我们平时最常使用的基本对象引用&#xff0c;JVM不会回收强引用类型对象&#xff0c;即使内存不足导致OOM也不会回收。实现一个强引用User user new User()软引用&#xff1a;内存空间足够的情况下&#xff0c;JVM不会回收软引用对象&#xff0c;如果内存空…

作者头像 李华
网站建设 2026/2/22 4:41:11

PyTorch官方安装命令在Miniconda-Python3.9中的实际应用

PyTorch 官方安装命令在 Miniconda-Python3.9 中的实践指南 在深度学习项目中&#xff0c;一个稳定、可复现的开发环境往往是成功的第一步。然而&#xff0c;许多开发者都曾经历过这样的场景&#xff1a;本地训练模型一切正常&#xff0c;换到服务器上却因版本冲突报错&#x…

作者头像 李华
网站建设 2026/2/24 19:21:16

PyTorch Hub模型加载失败?检查Miniconda-Python3.9网络配置

PyTorch Hub模型加载失败&#xff1f;检查Miniconda-Python3.9网络配置 在深度学习项目开发中&#xff0c;你是否曾遇到这样的场景&#xff1a;满怀期待地写下 torch.hub.load(pytorch/vision, resnet50)&#xff0c;结果却卡在下载环节&#xff0c;报出一连串 URLError 或 SSL…

作者头像 李华
网站建设 2026/2/8 18:47:02

Miniconda-Python3.9如何支持PyTorch与MLflow集成追踪实验

Miniconda-Python3.9 如何支持 PyTorch 与 MLflow 集成追踪实验 在现代机器学习研发中&#xff0c;一个常见的困扰是&#xff1a;昨天还能跑通的实验&#xff0c;今天却因为环境变动、依赖升级或配置差异而失败。更糟糕的是&#xff0c;当你试图复现某次“效果特别好”的训练结…

作者头像 李华
网站建设 2026/2/23 13:46:43

Mysql字段判空实用技巧

文章目录背景函数判空素材方案一、case函数方案二、nullif函数背景 在日常SQL处理中&#xff0c;如何对字段的空值进行处理&#xff0c;记录下来&#xff0c;方便备查。 函数判空 MySQL中可使用【ifnull】函数&#xff0c;该函数对空值可以判断&#xff0c;即数据库中值为【…

作者头像 李华