2.4.3用户定义的函数
标准C库提供了140多个预定义的函数,如果其中的函数能满足需求,则应使用它们。但用户经常需要编写自己的函数,尤其是在设计类的时候。无论如何,设计自己的函数很有意思,下面来介绍这一过程。前面已经使用过好几个用户定义的函数,它们都叫main()。每个C++程序都必须有一个main()函数,用户必须对它进行定义。假设需要添加另一个人用户定义的函数。和库函数一样,也可以通过函数名来调用用户定义的函数。对于库函数,在使用之前必须提供其原型,通常把原型放到main()定义之前。但现在您必须提供新函数的源代码。最简单的方法是,将代码放在main()的后面。程序2.5演示了这些元素:
程序2.5 ourfunc.cpp
//ourfunc.cpp--defining your own function #include<iostream> void simon(int); int main() { using namespace std; simon(3); cout<<"Pick an integer:"; int count; cin>>count; simon(count); cout<<"Done!"<<endl; return 0; } void simon(int n) { using namespace std; cout<<"Simon says touch your toes "<<n<<" times."<<endl; }
main()函数两次调用simom()函数,一次的参数是3,另一次的参数为变量count。在这两次调用之前,用户输入一个整数,用来设置count的值。这个例子没有在cout提示信息中使用换行符。这样将导致用户输入和提示出现在同一行中。下面是运行情况:
1、函数格式
在程序2.5中,simon()函数的定义与main()的定义采用的格式相同。首先,有一个函数头,然和是花括号中的函数体。可以把函数的格式统一为如下的情形:
type functionname(argumentlist)
{
statements
}
注意,定义simon()的源代码位于main()的后面,和C一样(但不同于Pascal),C++不允许将函数定义嵌套在另一个函数定义中。每个函数定义都是独立的,所有函数的创建都是平等的。
2、函数头
在程序2.5中,simon()函数的函数头如下:
void simon(int n)
开头的void表面simon()没有返回值,因此调用simon()不会生成可在main()中将其赋给变量的数字。因此,第一个函数调用方式如下:
simon(3);
由于simon()没有返回值,因此不能这样使用它:
simple=simon(3);
括号中的int n表面,使用simon时,应提供一个int 参数,n是一个新的变量,函数调用时传递的值将被赋给它。因此,下面的函数调用将3赋给simon()函数头中定义的变量n:
simon(3);
当函数体中的cout语句使用n时,将使用函数调用时传递的值。这就是为什么simon(3)在输出中显示3的原因所在。在示例运行中,函数调用simon(count)导致函数显示512,因为这正是赋给count的值。简言而之,simon()的函数头表面,该函数接受一个int 参数,不返回任何值。
下面复习一下main()的函数头:
int main()
开头的int表明,main()返回一个整数值,空括号(可以包含void)表明,main()没有参数,对于有返回值的函数,应使用关键字return来提供返回值,并结束函数,这就是为什么要在main()结尾使用下属语句的原因:
return 0;
这在逻辑上是以指的:main()返回一个int值,而程序员要求它返回整数0。但可能会产生疑问,将这个值返回到哪里了呢?毕竟,程序中没有哪个地方可以看出对main()的调用:
squeeze=main();
答案是,可以将计算机操作系统(如UNIX或Windows)看作调用程序。因此,main()的返回值并不是返回给程序的其他部分,而是返回给操作系统。很多操作系统都可以使用程序的返回值。例如 ,UNIX外壳脚本和Windows命令行批处理文件都被设计成运行程序,并测试它们的返回值(通常叫做退出值)。通常的约定是,退出值为0则意味着程序运行成行,为非零则意味着存在问题。因此,如果C++程序无法打开文件,可以将它设计为返回一个非零值。然后,便可以设计一个外壳脚本或批处理文件来运行该程序,如果该程序发出指示失败的消息,则采取其他措施。
关键字
关键字是计算机语言中的词汇。本章使用了4个C++关键字:int、void、return和double。由于这些关键字都是C++专用的,因此不能用作他用。也就是说,不能将return用作变量名,也不能把double用作函数名。不过可以把它们用作名称的一部分,如painter(其中包含int)或return_aces。另外,main不是关键字,由于它不是语言的组成部分。然而,它是一个必不可少的函数的名称。可以吧main用作变量名(在一些很神秘的以至于无法在这里介绍的情况中,将main用作变量名会引发错误,由于它在任何情况下都是容易混淆的,因此最好不要这样做)。同样,其他函数名和对象名都不能是关键字。然而,在程序中将同一个名称(比如cout)用作对象名和变量名会吧编译器搞糊涂。也就是说,在不使用cout对象进行输出的函数中,可以将cout用作变量名,但不能在同一个函数中同时将cout用作对象名和变量名。
2.4.4用户定义的有返回值的函数
我们在深一步,编写一个使用返回语句的函数。main()函数已经揭示了有返回值的函数的格式:在函数头中指出返回类型,在函数体结尾处使用return。可以用这种形式为在英国观光的人解决重量问题。在英国,很多浴室都已英石为单位,不像美国以磅或公斤为单位。一英石等于14磅,程序2.6使用一个函数来完成这样的转换。
//convert.cpp--converts stone to pounds #include<iostream> int stonetolb(int); int main() { using namespace std; int stone; cout<<"Enter the weight in stone:"; cin>>stone; int pounds =stonetolb(stone); cout<<stone<<" stone ="; cout<<pounds<<" pounds."<<endl; return 0; } int stonetolb(int sts) { return 14*sts; }
下面是该程序的运行情况:
在main()中,程序使用cin来给整型变量stone提供一个值。这个值被作为参数传递给stonetolb()函数,在该函数中,这个值被赋给变量sts。然后,stonetolb()用关键字return将14*sts返回给main()。这表明return 后面并给一定要跟一个简单的数字。这里通过使用较为复杂的表达式,避免创建一个新变量,将结果赋给该变量,然后将它返回。程序将计算表达式的值(这里为210),并将其返回。如果返回表达式的值很麻烦,可以采用更复杂的方式:
int stonetolb(int sts)
{
int pounds=14*sts;
return pounds;
}
这两个版本的返回的结果相同,但第二个版本更容易理解和修改,因为它将计算和返回分开了。通常,在可以使用一个简单变量的地方,都可以使用一个返回值类型与该常量相同的函数。例如,stonetolb()返回一个int值,这意味着可以以下面的方式使用该函数:
int aunt = stonetolb(20);
int aunts =aunt + stonetolb(10);
cout<<”Ferdie weighs ”stonetolb(16)<<” pounds.”<<endl;
在上述任何一种情况下,程序都将计算返回值,然后再语句中使用这个值。
这些例子表明,函数原型描述了函数的接口,即函数如何与程序的其他部分交互。参数列表指出了何种信息将被传递个函数,函数类型了返回值的类型。程序员有时将函数比作一个有出入它们的信息所指定的黑盒子,具有黑盒效应。
函数stonetobl()短小、简单,但包含了全部的函数特性,麻雀虽小五脏俱全:
l 有函数头和函数体;;
l 接受一个参数
l 返回一个值;
l 需要一个原型
可以把stonetolb()看作函数设计的标准格式,第七章和第八章将更详细地介绍函数。而本章的内容让读者能够很好地了解函数的工作方式及其愈合与C++匹配。
2.3.5 在多函数程序中使用using编译指令
在程序2.5中,两个函数中都包含下面一条using编译指令:
using namespace std;
这是因为每个函数都使用了cout,因此需要能够访问位于名称空间std中的cout定义。
当前通行的理念是,只让需要访问名称空间std的函数访问它是更好的选择,例如,在程序2.6中,只有main()函数使用了cout,因此没有必要让函数stonetolb()能够访问名称空间std。因此编译指令using被放在函数main()中,使得只有该函数能够访问名称空间std;
总之,让程序能够访问名称空间std的方法有很多种,下面是其中的4种。
将using namespace std;放在函数定义之前,让文件中所有的函数都能够使用名称空间std中所有的元素。
将using namespace std;放在特定的函数定义中,让该函数能够使用名称空间std中的所有元素。
在特定的函数中使用类似using std::cout;这样的编译指令,而不是using namespace std;,让该函数能够使用指定的元素,如cout。
完全不使用编译指令using,而在需要使用名称空间std中的元素时,使用前缀std::,如下所示:
std::cout<<”I’m using cout and endl from the std namespace”<<std::endl;
命名约定
C++程序员给函数、类和变量命名时,可以有很多种选择。程序员对风格的观点五花八门,这些看法有时就像公共论坛上的圣战。就函数名称而言,程序员有一下选择:
Myfunction( )
myfunction( )
myFunction( )
my_function( )
my_funct( )
选择取决于开发团体、使用的技术或库以及程序员个人的品味和喜好。因此凡是符合第三章将介绍的C++规则的风格都是正确的,都可以根据个人的判断而使用。
撇开语言是否允许不谈,个人的命名风格也是值得注意的——它有助于保持一致性和精确性。精确、让人一目了然的个人命名约定是良好的软件工程的标志,它是整个编程生涯中都会起到很好的作用。
2.5总结
C++程序有一个或多个被称为函数的模块组成。程序从main()函数(全部小写)开始执行,因此该函数必不可少。函数有函数头和函数体组成。函数头指出函数的返回值(如果有的话)的类型和函数期望通过参数传递给它的信息的类型。函数体有一系列位于花括号({})中的C++语言组成。
有多种类型的C++语句,包括下述6种。
n 声明语句:定义函数中使用的变量的名称和类型
n 赋值语句:使用赋值运算符(=)给变量赋值
n 消息语句:将消息发送给对象,激发某种行为。
n 函数调用:执行函数。被调用的函数执行完毕后,程序返回到函数调用语句后面的语句
n 函数原型:声明函数的返回类型、函数接受的参数数量和类型
n 返回语句:将一个值从被调用的函数那里返回到调用函数中
类是用户定义的数据类型规范,它详细描述了如何表示信息以及可对数据执行的操作。对象是根据类的规范创建的实体,就像简单变量是根据数据类型描述创建的实体一样。
C++提供了两个用于处理输入和输出的预定义对象(cin和cout),它们是istream和ostream类的实例,这两个类是在iostream文件中定义的。为ostream类定义的插入运算符(<<)使得将数据插入到输出流成为可能;为istream类定义的抽取运算符(>>)能够从输入流中抽取信息。cin和cout都是智能对象,能够根据程序上下文自动将信息从一种形式转换为另一种形式。
C++可以使用大量的C库函数。要使用库函数,应当包含提供该函数原型的头文件。