刚开始学编程的时候我使用了Python,用惯了Python的众多内建函数(所以它才会被称作为“具备电池的语言 battery-included language”),转型到C++的时候难免会有些不适应。今天就让我们实现一下那些“被抹灭掉的函数”,顺便帮一下像我一样刚刚踏入C++的世界的时候的初学者们吧。
为什么这是第一篇文章,实现的是
input函数而不是最常用的abs函数呢?这其实是有个人原因的:我初次踏进C++的土地时,第一次折磨我的函数便是input——我哪儿都找不到相应的实现。我觉得应该也有人跟我一样。
今天的目标
- 能够使用C++实现Python内建的
input()函数的所有功能。- 包括
prompt提示符; - 回传值等等。
- 包括
温故而知新
首先我们得知道Python的input函数带给了使用者什么的功能(说白了就是它是用来干什么的)。参考Python的官方文档,他写着:
如果存在prompt实参,则将其写入标准输出,末尾不带换行符。接下来,该函数从输入中读取一行,将其转换为字符串(除了末尾的换行符)并返回。当读取到EOF时,则触发
EOFError。例如:>>> s = input('--> ') --> Monty Python's Flying Circus >>> s "Monty Python's Flying Circus"如果加载了
readline模块,input()将使用它来提供复杂的行编辑和历史记录功能。
引发一个审计事件builtins.input附带参数prompt。
在成功读取输入之后引发一个审计事件builtins.input/result附带结果。
Python的官方文档可能用了机器翻译,有些小伙伴们可能看不懂这种由英语硬生生翻译成中文的语文。让我们解刨一下原文,再把它用大白话说出来。首先让我们看看文档的原文:
If the prompt argument is present, it is written to standard output without a trailing newline. The function then reads a line from input, converts it to a string (stripping a trailing newline), and returns that. When EOF is read,
EOFErroris raised. Example:>>> s = input('--> ') --> Monty Python's Flying Circus >>> s "Monty Python's Flying Circus"If the
readlinemodule was loaded, theninput()will use it to provide elaborate line editing and history features.
Raises an auditing eventbuiltins.inputwith argumentpromptbefore reading input
Raises an auditing eventbuiltins.input/resultwith the result after successfully reading input.
这什么意思呢?根据我们自己的翻译,其内容大意(大致上)是:
- 如果
prompt值非零,且内容为可打印(printable)字串(string)(也就是prompt可以用在print()函数里头),这个函数首先会打印prompt出来; - 然后Python会让你输入一行字串(记住,就一行),当你按下回车(Enter键/return键)输入就会立刻终止。
- 最后这个函数会以字符串的形式返回所有你刚才所输入的字符,不包括结尾的回车字符/换行字符
'\n'/'\r'。
剩下的可以管也可以不管。但是通常现在的终端机模拟APP(terminal-simulating application)已经帮你管好了。所以我们就一切从简。
最后让我们看看他的官方文档字串(docstring)的参数列表:
input(prompt='', /)
懂Python的朋友都应该知道,prompt参数后面的那个斜杠(slash)是为了美观而加上去的,直译器会把这个部分忽略掉。
着手开始实现
好了那么我们先准备一下我们的C++编译器,好让我们在编程完毕后顺利编译。不管你们用的是自由软体基金会的GCC/G++编译器也好,微软的Visual C++ (MSVC) 编译器也罢,只要支持c++11以及能够能产生可运行文件(executable)便行。我们这次实现对于编译器的要求并不是太高。
引入头文档
在C/C++的世界里,头文件决定一切。当你什么头文件(header file)都不引入(include)的时候,你顶多只能声明(declare)变量(variable)、函数(function)、类(class)等,做做四则运算(arithmetic operation)而不能输出结果。甚至Python、Ruby等语言是内建(builtin)的函数跟类,在C++中你也必须要#include <某个头文件.h>。
我们这次要引入的头文档也不算太多,如果是C++的话就只需要两个:
#include <iostream>
#include <string>
iostream,表面意思为“输入/输出流”(input/output stream),“流”是指从输入/输出设备中读写到的字符。这个头文档对一个名为basic_iostream的模板(template)定义,实现了格式化的输入/输出。
string,顾名思义,这个头文件定义了C++中字串符的操作,跟Python的内建str类有异曲同工之妙(不同点是使用C++的std::string类需要导入,也就是像Python的import指令差不多;Python允许程序自由使用单引号''/双引号""作为字串的定义工具,C++只允许双引号""因为单引号是用来定义字元char类的,这些我们以后有机会细聊)。
撰写程式代码
那么,我们现在终于可以开始变成了。
定义一个input()函数
Python的input()是在接收完用户的输入之后,以字符串的形式返回的嘛(如上所述)。C++中的字符串类是std::string,于是返回值也一定是std::string。
C++定义一个函数的方法是:[函数回传值] [函数名称,不能与其他变量、类、枚举、模板等有冲突] ( [参数类型,如有] [参数名称,如有。不能和其他参数名字一样,但是可以与变量、类等名称发生冲突,不过个人不建议这么做] ) { ... }。
所以,代码如下:
std::string input(std::string prompt = "")
{
}
由于Python input(prompt)的默认(default)参数(parameter)是空字串"",所以我们的C++程序也要效仿Python。
输出 prompt
不管程序有没有在呼叫(call)这个input()函数时有没有指明prompt参数,我们还是得打印prompt的。Python的内建打印方法print,可以用C++的std::cout代替(不过不完全能,以后我们逮住机会再实现一下print函数,还是挺有学习价值的),代码如下:
std::string input(std::string prompt = "")
{
std::cout << prompt;
}
C++的std::cout默认输出后不带换行符,这对我们来说是个福音——因为不用再进行调整了。
到这里,程序应该能够把prompt打印至终端(或者其他输出设备)上。然后就没它什么事了。
留意这个时候千万不能使用input函数的回传值。由于我们没有明确地(explicitly)指明这个函数该回传什么。正常来说,回传值为void函数(也就是Python里的-> None函数)在呼叫完毕后会return;,也就是不回传任何值。直接访问这个函数的回传值会导致错误。但是由于我们主动把它设成一个回传值为std::string的函数,在编译时如果把它储存到一个变量还尚且没问题。但不管程序是直接还是间接地访问(access)这个变量(例如想知道其内容)或者这个函数的回传值,都会激发一个名为记忆体区段错误(segmentation fault)的运行时错误(runtime error)。程序戛然而止,不管现在正在运行什么重要的线程(thread)。显然我们跟用户都不想这种事情发生,也难怪编译器在编译时会蹦出来一条警告:Non-void function should return a value(“非无返回值的函数应该要返回一个值”)。
那,我们该怎么办呢?别急,我们现在正要去实现其功能。
输入功能实现
呼叫input()函数通常只有一个目的:输入字符。首先让我们看一看全部代码:
std::string input(std::string prompt = "")
{
// Prints the `prompt` out in the console.
std::cout << prompt;
// Defines a variable, storing the user input.
std::string source = "";
// Retrieve the user input, by using the `std::getline` function and
// the `std::cin` method, with the new input value stored in the
// `source` variable.
std::getline(std::cin, source);
// And finally, return the `source` variable.
return source;
}
- 使用字符串类型的
source变量来储存用户的输入字符。 - 使用
std::getline来获取用户的输入字符。 - 最后,返回
source。
小伙伴们可能会问:网上的教程都写着std::cin >> 什么什么的,为什么我们不用。这其实涉及到某些原因:getline把所有用户的输入全都变成字符串,刚刚好与我们今天的目标一样,也与Python的设计理念相似。反观std::cin,只要提供变量名称,你输入整数、浮点数都可以;这个并不利于我们的程序:隐式(implicit)类型转换令变量不易回传——它可以什么都是,并不单单局限于std::string类。
实现 main函数
不想某些脚本语言(scripting languages),在一个标准的C/C++程式里,你不能直接在全局命名空间域(global namespace)里头撰写程式码,否则会招惹错误。你一定得写个int main()函数的实现(一说是void main()也行,但是标准C/C++建议使用int作为回传值并且在函数的最后回传0),相当于Python的if __name__ == "__main__"了。在我们这里,我们效仿上面Python官方档案的标准例子:
int main()
{
// Calls the input function.
std::string s = input("--> ");
// Prints the return value, with a trailing newline.
std::cout << s << std::endl;
// Returns 0 to the operating system.
return 0;
}
记得在std::cout的后面加上std::endl,因为不想print函数,C++的std::cout不会帮程序自动加一个分行符号\n。你要自己加上去才行,否则会很难看。还有也要记得回传0给作业系统(或者那个线程)。
成果
现在让我们来看看成果:
完整的代码
代码如下图,如下:

运行效果
接下来,请把这些代码储存,然后使用编译器来编译(我使用的时g++编译器)。留意g++在微软的Windows操作系统中,默认它不在环境变量当中。如果是如此,你得输入这个编译器的绝对路径(absolute path),例如C:\<一大堆>\g++.exe什么的。或者你可以使用控制台把它加入至环境变量当中。具体办法可以网上搜搜看,这里就不作赘述了。
苹果macOS的用户也请留意,使用g++之前你一定要先装Xcode并且下载Xcode命令列界面工具(Xcode Commandline Tools),才能正常使用g++编译器。
废话不说,这是我们在Windows上运行的小成果:
可以与Python的作对比,发现其实差的不算太多。

其实也是一样的嘛。
后记
为什么不用using namespace std;
稍微懂C++的朋友一定见过这句指令:
using namespace std;
这句指令的意思是说,我们不用std命名空间了,我们把它里边所定义的所有变量跟函数都挪过去全局命名空间里。这样做乍看之下好像没有那么累赘了,确实是没那么累赘写代码也省事,但是它的道理跟Python的from 模组名称 import *是一样的:若不管理好引入的头文档资料,新来的定义会把之前的给覆盖掉。这样做非常麻烦,原本省事的目的没达到还惹了一连串的duplicate reference或者invalid name错误。最直白的例子是如果你用<windows.h>头文档,当中有min和max两个函数,是用来进行仅适用于Windows平台的大小比较作业。当你又手痒引入了<cmath.h>头文档,然后用using namespace std;作死,估计要么就是cmath.h里的min和max不能用;要么就是windows.h里的不能用,要么就是报错。还好这次的小项目十分小,声明一下也无妨,但我想表达的是这真不是编程上的一个良好习惯。
本文指导如何用C++实现Python的input()函数,包括prompt处理和用户输入的获取,通过实例演示如何在C++中模拟Python的input行为,适合C++初学者了解基本概念。
842

被折叠的 条评论
为什么被折叠?



