赞
踩
刚开始学编程的时候我使用了Python,用惯了Python的众多内建函数(所以它才会被称作为“具备电池的语言 battery-included language”),转型到C++的时候难免会有些不适应。今天就让我们实现一下那些“被抹灭掉的函数”,顺便帮一下像我一样刚刚踏入C++的世界的时候的初学者们吧。
为什么这是第一篇文章,实现的是
input
函数而不是最常用的abs
函数呢?这其实是有个人原因的:我初次踏进C++的土地时,第一次折磨我的函数便是input
——我哪儿都找不到相应的实现。我觉得应该也有人跟我一样。
input()
函数的所有功能。
prompt
提示符;首先我们得知道Python的input
函数带给了使用者什么的功能(说白了就是它是用来干什么的)。参考Python的官方文档,他写着:
如果存在prompt实参,则将其写入标准输出,末尾不带换行符。接下来,该函数从输入中读取一行,将其转换为字符串(除了末尾的换行符)并返回。当读取到EOF时,则触发
EOFError
。例如:>>> s = input('--> ') --> Monty Python's Flying Circus >>> s "Monty Python's Flying Circus"
- 1
- 2
- 3
- 4
如果加载了
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,
EOFError
is raised. Example:>>> s = input('--> ') --> Monty Python's Flying Circus >>> s "Monty Python's Flying Circus"
- 1
- 2
- 3
- 4
If the
readline
module was loaded, theninput()
will use it to provide elaborate line editing and history features.
Raises an auditing eventbuiltins.input
with argumentprompt
before reading input
Raises an auditing eventbuiltins.input/result
with the result after successfully reading input.
这什么意思呢?根据我们自己的翻译,其内容大意(大致上)是:
prompt
值非零,且内容为可打印(printable)字串(string)(也就是prompt
可以用在print()
函数里头),这个函数首先会打印prompt
出来;'\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
里的不能用,要么就是报错。还好这次的小项目十分小,声明一下也无妨,但我想表达的是这真不是编程上的一个良好习惯。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。