内容简介1、第一部分第七课:函数效应,分"/>

C++探索之旅第一部分第七课:函数效应,分而治之

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++探索之旅第一部分第七课:函数效应,分而治之相关的知识,希望对你有一定的参考价值。

技术分享


内容简介

1、第一部分第七课函数效应,分而治之

2、第一部分第八课预告:传值引用,文件源头



函数效应,分而治之


上一课《【C++探索之旅】第一部分第六课:控制流程,随心所至》中,我们学习了条件语句和循环语句。


这两种语句也算是算法的核心了。在更早的课程中,我们学习了变量。这些都是所有编程语言的必备元素。


这一课我们又要学习一个几乎所有编程语言都有的极重要元素:

函数


C++的所有程序都或多或少用到函数,到目前为止,你其实也已经用了好多次了,不过你可能只缘身在此山中,还云深不知处。


函数的功用是:

将程序切分成更小的可重用的单元,有点像砖块。一旦砖块制作完毕,程序员所要做的就是用砖块来造东西。


我们砌一堵墙,盖一座小屋,或者摩天大楼,都需要不少砖块。用函数构建我们的程序就似用砖块搭建,而且如果拆除了,之后这些砖块还可以重复使用。


慢慢你就能体会了。编程之美是需要时间去印证的,如果只远观而不"亵玩",没有动手实验,是不能进步的。


首先我们就挽起袖子,和好水泥,来学习如何制作"砖块"(函数)吧。



函数的创建和使用


从这个课程的最初,我们已经使用了函数了。而且到目前为止都是那个函数:main函数(是我,是我,还是我...)。


这是C++程序的入口,程序是从main函数开始执行的。如下所示:


#include <iostream>
using namespace std;

int main() // main函数开始,程序的开始
{
  cout << "Hello World !" << endl;
  return 0;
} // main函数的结束,程序也随之结束


以上的程序实际是从第四行开始,在第八行结束。也就是说,此程序的所有动作都是在一个函数里完成的。我们并没有跳转到其他地方,没有出这个main函数,而是按顺序执行main函数中的各句指令。


既然我这样说,聪明如你应该料想到了:我们可以写其他的函数,把一个复杂的程序分割成相对独立的区块。


是的。但是为什么要这么做呢?


虽然说,把所有代码都放在一个main函数里是完全可以的,但这并不是一个好习惯。


假设我们要开发一个大型3D游戏。


大型3D游戏可是很复杂的,代码数动辄上万行。如果我们把这么多代码都一股脑儿放到main函数里,那么是很容易迷失在茫茫码海中的。


比较理想的方式是在某个地方存放一小块代码,比如用于移动人物;另一个地方存放另一块代码,用于加载关卡,等等。


把代码分成函数可以方便管理,使我们的代码更容易被别人理解,我们自己回看时也不至于迷失了方向。


而且,假如你们是好几个程序员一起协同开发,那么函数可以让你们更好地分配工作,比如某个人负责哪些功能开发,就需要写哪些函数。


但这并不是函数的全部益处。


举个例子:我们要计算平方根。上一课我们已经学习了怎么做,我们使用了数学库中的sqrt这个函数。


类似开平方根这样常用的代码块,如果包装成一个函数,那么在不同地方使用时就不需要把相同的代码重新拷贝一次了,只需要写一下函数名就可以了。


所以函数使我们可以重复使用已有的代码块,极大地提高效率。


函数简介


函数包含完成特定任务的一个代码块。它接收需要处理的数据,处理之,然后返回一个值。


可以把函数想象成一台制作香肠的机器,在输入那一头你把猪装进去,输出那一头就出来香肠了。这酸爽。


技术分享


输入函数的数据称为参数,而函数输出的数据称为返回值。如下图所示:


技术分享


你是否还记得我们上一课列出的多个数学函数中的pow函数,是用于计算次方值的。例如2的3次方,可以用pow(2, 3)来计算。


如果我们使用刚才所说的专业术语(参数,返回值),那么pow函数的基本介绍可以如下所示:


  1. 接收两个参数x和y

  2. 进行数学计算(x的y次方)

  3. 返回值就是计算结果


如下图所示:


技术分享


定义函数


好了,该放手一搏了。上面看了一些函数的例子,我们自己来定义一个函数吧。


首先,C++中所有的函数基本都遵循以下模板:


type functionName(arguments)
{
    // Body : Instructions
}


上面的是英语的表述法,如果翻成中文就是:


类型  函数名(参数)

{

    // 函数体:指令

}


关于这个模板我们需要掌握四点:

  1. 函 数类型:对应输出类型,也可以把其看做函数的类型。和变量类似,函数也有类型,这类型取决于函数返回值的类型。如果一个函数返回一个浮点数(带小数点 的),那么自然我们会将函数类型定为float或者double;如果返回整数,那么我们一般会将类型定为int或long。但是我们也可以创建不返回任 何值的函数。

  2. 函数名:这是你的函数的名字。我们已经见过不少函数名了,比如main,sqrt,pow。你可以给你的函数起任意名字,只要遵从给变量命名的相同的规则就好。

  3. 函数的参数(对应输入):参数位于函数名之后的圆括号内。这些参数是函数要用来做操作(运算)的数据。你可以给函数传入任意数量的参数,也可以不传入任何参数(例如main函数)。

  4. 函数体:大括号规定了函数的起始和结束范围。在大括号中你可以写入任意多的指令。


注意

在 C语言中,同一个程序里不允许同名的函数。但是C++中允许有同名函数,称为函数重载。只需要它们的参数不同(注意是参数不同,如果返回值不同而参数一样 不是函数重载,也不能通过编译)。例如在C++中,int multiplication(int a, int b) 和 double multiplication(double c, double d) 就是不同的函数,尽管它们的函数名一样,但是参数列表不一样。但是如果定义两个函数如下:int multiplication(int a, int b) 和 double multiplication(int c, int d),编译是会出错的,因为它们的参数一样,只是返回值不一样,是不能算不同函数的。


我们自己来定义一个函数:


int addTwo(int number)
{
    int value(number + 2);
    // 在内存中申请一个int类型的"抽屉",起名叫value
    // 将参数中接收到的number进行加2操作
    // 将加法的结果存入value里面
    
    return value;
    // 指定value为函数的返回值
}


注意:在参数的那个括号和函数体的大括号后面都没有分号哦!


分析此函数


根据我们之前的解释,你应该明白第一行是干什么了吧。就是定义一个函数,名叫addTwo(英语"加2"的意思),接收一个int型参数作为输入,操作结束之后会返回一个int型值。如下图:


技术分享


return value; 那一行是什么意思呢?


return是英语"返回"的意思,所以这一行指令的作用就是将value作为此函数的返回值返回。addTwo的参数number就类似之前那个图中放入香肠制造机的猪,value就类似输出的香肠。


value的类型必须是int,因为在函数定义的第一行的返回值类型是int。


函数调用


我们的函数定义完毕,接下来该是使用它的时候了。函数的使用也有一个术语,叫做:函数调用。

#include <iostream>
using namespace std;

int addTwo(int number)
{
    int value(number + 2);
    return value;
}

int main()
{
    int a(2), b(3);
    
    cout << "a的值是 " << a << endl;
    cout << "b的值是 " << b << endl;
    
    b = addTwo(a);  // 函数调用
    
    cout << "a的值是 " << a << endl;
    cout << "b的值是 " << b << endl;
    
    return 0;
}


运行以上程序,显示:


a的值是 2

b的值是 3

a的值是 2

b的值是 4


在调用函数addTwo之后,a的值没有改变;b的值改变了,等于a的值加上2。


详细解释


现在我们来看一个程序,包含一个multipleTwo函数,用于计算一个数的两倍的值。


我们暂时把multipleTwo函数写在main函数之前,如果放在main函数之后会出错,以后的课程我们会解释为什么。

#include <iostream>
using namespace std;

int multipleTwo(int number)
{
    return 2 * number;
}

int main()
{
    int initial = 0, twice = 0;
    
    cout << "请输入一个整数... ";
    cin >> initial;
    twice = multipleTwo(initial);
    cout << "这个数的两倍是 " << twice << endl;
    
    return 0;
}

我们的程序是从main函数开始运行的,这个大家已经知道了。


我们首先请求用户输入一个整数,将其值传递给multipleTwo函数,并且把multipleTwo函数的返回值赋给twice这个变量。


仔细看下面这一行,这是我们最关心的一行代码,因为正是这一行调用了我们的multipleTwo函数。


twice = multipleTwo(initial);


在括号里,我们将变量initial当做输入传递给函数,也正是这个变量,函数将其用于内部的处理。


这个函数返回一个值,就是twice这个变量。


其实这一行中,我们就是命令电脑:“让multipleTwo函数给我计算initial的两倍的值,并且将结果储存到twice这个变量中”。


详细的分步解释


也许对于初学者,理解起来还是有些许困难。


不用担心,我相信通过下面的分步解释,大家会明白得更透彻。


这个特殊注释的代码向大家展示了程序的运行顺序:


#include <iostream>
using namespace std;

int multipleTwo(int number)   // 6
{    
    return 2 * number;    // 7
}

int main()   // 1
{    
    int initial = 0, twice = 0;   // 2    
    
    cout << "请输入一个整数... ";   // 3    
    
    cin >> initial;   // 4    
    
    twice = multipleTwo(initial);   // 5    
    
    cout << "这个数的两倍是 " << twice << endl;  // 8    
    
    return 0;  // 9
}


上面的编号表示了执行的顺序:1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9


  1. 程序从main函数开始执行

  2. 在main函数中的命令一行一行地被执行

  3. 执行cout输出

  4. 执行cin读入数据,赋值给变量initial

  5. 读入指令... 调用multipleTwo函数了,因此程序跳到上面的multipleTwo函数中去执行

  6. 我们运行multipleTwo函数,并接受一个数作为输入(number)

  7. 我们对number做运算,并且结束multipleTwo函数,return意味着函数的结束,并且返回一个值。将返回值赋给twice变量。

  8. 我们重新回到main函数的下一条指令,用cout输出

  9. 又一个return,这次是main函数的结束,于是整个程序运行完毕。


变量initial被传值给multipleTwo的参数number(另一个变量),称为值传递。当然其实原理是做了一份变量initial的拷贝,把拷贝赋值给了number。


这里如果我们把initial改名为number也是可以的,并不会与函数multipleTwo的参数number冲突。因为参数number是属于multipleTwo这个函数的专属变量。


多个参数


上面的addTwo函数只有一个参数,我们也可以定义有多个参数的函数。其实之前见过的pow函数和getline函数就有两个参数。


int addition(int a, int b)
{
    return a+b;
}
double multiplication(double a, double b, double c)
{
    return a * b * c;
}


以上所定义的两个函数中,我们合理地偷懒了:将参数运算的结果直接作为返回值,而没有定义例如之前在addTwo函数中用到的value这样的变量。这样我们的代码也达到了最简化,我们是鼓励这样做的。程序员要会"偷懒"。


上面的第一个函数addition(英语"加法,相加"的意思)接收两个int型参数,返回它们的相加值。


第二个函数multiplication(英语"乘法,相乘"的意思)接收三个double型参数,返回它们的相乘值。


我们也可以定义参数类型不一样的函数。例如:double different(int a, double b);


无参数


我们的函数也可以没有参数。那么在括号里就不加任何东西。


例如:

string getName()
{    
     cout << "请输入你的名字 : ";
     string name;
     cin >> name;
     return name;
}


无返回值的函数


函数也可以没有返回值,那么在定义的时候要将函数返回值类型设为void(英语"虚空,无"的意思)。不用加return语句了。例如:

void sayHello()
{    
    cout << "Hello!" << endl;    
    // 因为没有返回值,也就没有return语句了
}
int main()
{
    sayHello();
    // 因为函数sayHello不返回任何值
    // 我们调用的时候也不会将其赋给某个变量了
    
    return 0;
}



实例


下面我们会一起看几个函数的实例,以便读者对函数有更深入的了解。我们尽量展示不同情况,使大家看到可能出现的各种函数类型。


欧元/人民币转换


最近欧元兑换人民币的汇率还是很稳定的在7左右徘徊,为什么当年(2009)小编出来法国时,欧元的汇率那么高(10),现在要换人民币却只有7。唉,就是辣么不逢时。


我们就来写一个函数,用于转换欧元到人民币。


查了一下最新的汇率:

1欧元 = 7.0992 人民币元

#include <iostream>
using namespace std;

double conversion(double euros)
{
 double rmb = 0;
 rmb = 7.0992 * euros;
 return rmb;
}

int main()
{
    cout << "10 欧元 = " << conversion(10) << "人民币" << endl;
    cout << "50 欧元 = " << conversion(50) << "人民币" << endl;
    cout << "100 欧元 = " << conversion(100) << "人民币" << endl;
    cout << "200 欧元 = " << conversion(200) << "人民币" << endl;
    return 0;
}

你也可以写一个人民币转换为欧元的小程序。


惩罚


接下来看一个函数,这个函数不会返回任何值,所以类型是void。这个函数会根据传入的参数在屏幕上显示一定次数的信息。这个函数只有一个参数,那就是显示惩罚语句的次数:

#include <iostream>
using namespace std;

void punish(int lineNumber)
{
 int i;
 for (i = 0 ; i < lineNumber ; i++)
 {
     cout << "我不应该有钱任性" << endl;
 }    
}

int main(int argc, char *argv[])
{    
 punish(5);
 return 0;
}


显示结果如下:


我不应该有钱任性

我不应该有钱任性

我不应该有钱任性

我不应该有钱任性

我不应该有钱任性


矩形面积


矩形的面积很容易计算:长 x 宽。长度乘以宽度。


我们来写一个求矩形面积的函数,它有两个参数:矩形的长和矩形的宽。返回值是矩形的面积:


#include <iostream>
using namespace std;

double rectangleArea(double length, double width)
{
    return length * width;
}

int main()
{
    cout << "长是10,宽是5的矩形面积是 " << rectangleArea(10, 5) << endl;
    cout << "长是3.5,宽是2.5的矩形面积是 " << rectangleArea(3.5, 2.5) << endl;
    cout << "长是9.7,宽是4.2的矩形面积是 " << rectangleArea(9.7, 4.2) << endl;
    
    return 0;
}


显示结果:


长是10,宽是5的矩形面积是 50.000000

长是3.5,宽是2.5的矩形面积是 8.750000

长是9.7,宽是4.2的矩形面积是 40.740000


我们可以直接在函数里显示 长,宽和计算所得的面积吗?


当然可以。这样的情况下,函数就不必返回任何值了,函数计算出矩形面积,然后直接显示在屏幕上:

#include <iostream>
using namespace std;

void rectangleArea(double length, double width)
{
    double area = 0;
    area = length * width;
    cout << "长为 "<< length << " 宽为 " << width << " 的矩形的面积是 " << area << endl;
}

int main()
{
    rectangleArea(10, 5);
    rectangleArea(3.5, 2.5);
    rectangleArea(9.7, 4.2);
    
    return 0;
}


我们可以看到,cout在函数体内被调用,显示的结果和之前把cout放在main函数里是一样的。只不过我们用的方法不一样罢了。


菜单


我们来写一个餐馆菜单的例子。


#include <iostream>
using namespace std;

int menu()
{
    int choice = 0;
    
    while (choice < 1 || choice > 4)
    {
        cout << "菜单 :" <<endl;
        cout << "1 : 北京烤鸭" <<endl;
        cout << "2 : 麻婆豆腐" <<endl;
        cout << "3 : 鱼香肉丝" <<endl;
        cout << "4 : 剁椒鱼头" <<endl;
        cout << "您的选择是 ? " <<endl;
        cin >> choice;
    }
    
    return choice;
}

int main()
{
    switch (menu())
    {
        case 1:
            cout << "您点了北京烤鸭" << endl;
            break;
        case 2:
            cout << "您点了麻婆豆腐" << endl;
            break;
        case 3:
            cout << "您点了鱼香肉丝" << endl;
            break;
        case 4:
            cout << "您点了剁椒鱼头" << endl;
            break;
    }
    
    return 0;
}


这个程序还可以改进:你可以在用户输入一个错误的数字时显示一个错误信息,而不是直接继续让其点单。



总结


  1. 函数包含具有特定目的的一段代码。

  2. 每个C++程序至少有一个函数:main函数。这是程序的入口。

  3. 把程序切分为有特定作用的各个函数是管理代码的好方式。

  4. 程序中,我们可以多次调用同一个函数。

  5. 一个函数可以接收数据(通过参数),也可以返回数据(通过return)。



第一部分第八课预告


今天的课就到这里,一起加油吧!

下一课我们学习:传值引用,文件源头

本文出自 “程序员联盟 frogoscar” 博客,请务必保留此出处http://4526621.blog.51cto.com/4516621/1738449

以上是关于C++探索之旅第一部分第七课:函数效应,分而治之的主要内容,如果未能解决你的问题,请参考以下文章

C++探索之旅第一部分第九课:数组威武,动静合一

C++探索之旅第二部分第一课:面向对象初探,string的惊天内幕

C++探索之旅第一部分第八课:传值引用,文件源头

C++探索之旅第一部分第十一课:小游戏,猜单词

C++探索之旅第一部分第十一课:小游戏,猜单词

C++探索之旅第一部分第十课:文件读写,海阔凭鱼跃