目录

空函数

调用函数

调用

执行流程

命名空间


在创建函数时,必须编写其定义。所有函数定义包括以下组成部分:

  • 名称:每个函数都必须有一个名称。通常,适用于变量名称的规则同样也适用于函数名称。
  • 形参列表:调用函数的程序模块可以向其发送数据。形参列表是保存传递给函数的值的变量列表。如果没有值传递给函数,则其形参列表为空。
  • 主体:函数的主体是处理函数正在执行的任务的一组语句。这些语句包含在一组大括号中。
  • 返回类型:函数可以将值发送回调用它的程序模块。返回类型是要发送回的值的数据类型。

图 1 显示了标有各组成部分的简单函数。

图 1 函数的组成部分

定义中的第一行称为函数头。现在来仔细看一看这 3 个部分。第一个部分是函数的返回类型,其次是函数名称,函数头的末尾则是一对括号。如果该函数有任何参数,则它们将排列在该括号中。当然,即使形参列表为空,括号也必须保留,正如图 1 所示。

空函数

如前所述,函数可以返回一个值。教程中介绍过的所有程序中的 main 函数都被声明为向操作系统返回一个 int 值。”return 0;” 语句使得当 main 函数完成执行时返回值 0。

然而,并不是所有函数都一定要返回值。某些函数只需执行一个或多个语句,然后返回。在C++中,这样的函数称为空函数。下面显示的 displayMessage 函数就是一个空函数示例:

void displayMessage(){cout << "Hello from the function displayMessage.\n";}

该函数的名称是 displayMessage,意思是“显示消息”,它是一个描述性的名称,说明了函数的功能。函数就应该按这种方式命名,即通过名称揭示其功能。因为该函数不需要接收任何信息以执行其任务,所以它的括号中没有形参列表。

该函数的返回类型是 void。这意味着函数在完成执行后不返回值,并返回到调用该程序的部分。因为没有返回值,所以不需要 return 语句。当函数中的语句己经完成执行并且遇到结束函数的封闭大括号时,程序将自动返回。

调用函数

调用函数将导致函数的执行。函数 main 在程序启动时自动调用,但所有其他函数必须由函数调用语句执行。当一个函数被调用时,程序分支到该函数并执行其主体中的语句。

现在来看一个程序,它包含了两个函数:main 和 displayMessage。

// This program has two functions: main and displayMessage.#include using namespace std;// Function prototypevoid displayMessage();//mian函数int main(){cout << "Hello from main.\n";displayMessage(); // Call.displayMessagecout << "Now we are back in the main function again. \n";return 0;}void displayMessage(){cout << "Hello from the displayMessage function.\n";}

程序输出结果:

Hello from main.
Hello from the displayMessage function.
Now we are back in the main function again.

调用

与所有 C++ 程序一样,该程序是从 main 函数开始执行的,其他函数只有在它们被调用时才执行。在上面程序中,函数 displayMessage 由 main 函数中的以下语句调用:

displayMessage();

请注意函数调用的形式,它只是函数的名称,后跟一组括号和分号。现在使用它来和函数头比较一下:

  • 函数头 -> void displayMessage;
  • 函数调用-> displayMessage();

函数头是函数定义的一部分。它声明函数的返回类型、名称和形参列表。它不能用分号终止,因为函数的主体定义要跟在它后面。

函数调用是一个执行该函数的语句,所以它像所有其他 C++ 语句一样,以分号终止。

请注意,函数调用不会包括返回类型。

可能有人会奇怪,上面程序中的第 6 行语句是做什么用的?它被称为函数原型,它的任务很简单,就是让编译器知道,在程序的后面将出现这个函数。它看起来很像是函数头,但它其实是一个语句,所以是以分号结束的。

执行流程

现在来看一下上面程序的执行流程。理所当然,它是从 main 函数开始的。当遇到调用 displayMessage 函数的语句时,程序分支到该函数并执行其语句。一旦 displayMessage 完成执行,则程序将返回到 main 函数,继续执行函数调用行后面的语句,如图 2 所示。

函数调用语句可用于诸如循环、if 语句和 switch 语句之类的控制结构中。例如,下面程序就把 displayMessage 函数调用放在了循环中:

#include using namespace std;// Function prototypevoid displayMessage();int main(){cout << "Hello from main.\n";for (int count = 0; count < 3; count++){displayMessage() ; // Call displayMessage}cout << "Back in function main again.\n";return 0;}void displayMessage(){cout << "Hello from the function displayMessage.\n";}

程序输出结果:

Hello from main.
Hello from the displayMessage function.
Hello from the displayMessage function.
Hello from the displayMessage function.
Back in function main again.

在程序中可以有很多函数和函数调用,且函数之间也可以相互调用,如下面程序所示,它具有 3 个函数:main、deep和deeper:

#include using namespace std;// Function prototypevoid deep();void deeper();int main(){cout << "工 am starting in function main.\n";deep(); // Call function deepcout << "Now I amback in function main again.\n";return 0;}void deep(){cout << "I am now inside the function deep.\n";deeper();// Call function deepercout << "Now I am back in deep. \n";}void deeper(){cout << "I am now inside the function deeper.\n";}

程序输出结果:

I am starting in function main.
I am now inside the function deep.
I am now inside the function deeper.
Now I am back in deep.
Now I am back in function main again.

此程序中,函数 main 只调用函数 deep。然后由 deep 调用 deeper,程序釆用的路径如图 3 所示。

图 3 以分层形式调用函数的程序执行路径

命名空间

一个中大型软件往往由多名程序员共同开发,会使用大量的变量和函数,不可避免地会出现变量或函数的命名冲突。当所有人的代码都测试通过,没有问题时,将它们结合到一起就有可能会出现命名冲突。

例如小李和小韩都参与了一个文件管理系统的开发,它们都定义了一个全局变量 fp,用来指明当前打开的文件,将他们的代码整合在一起编译时,很明显编译器会提示 fp 重复定义(Redefinition)错误。

为了解决合作开发时的命名冲突问题,C++引入了命名空间(Namespace)的概念。请看下面的例子:

  1. namespace Li{ //小李的变量定义
  2. FILE* fp = NULL;
  3. }
  4. namespace Han{ //小韩的变量定义
  5. FILE* fp = NULL;
  6. }

小李与小韩各自定义了以自己姓氏为名的命名空间,此时再将他们的 fp 变量放在一起编译就不会有任何问题。

命名空间有时也被称为名字空间、名称空间。

namespace 是C++中的关键字,用来定义一个命名空间,语法格式为:

namespace name{
//variables, functions, classes
}

name是命名空间的名字,它里面可以包含变量、函数、类、typedef、#define 等,最后由{ }包围。

使用变量、函数时要指明它们所在的命名空间。以上面的 fp 变量为例,可以这样来使用:

  1. Li::fp = fopen(“one.txt”, “r”); //使用小李定义的变量 fp
  2. Han::fp = fopen(“two.txt”, “rb+”); //使用小韩定义的变量 fp

::是一个新符号,称为域解析操作符,在C++中用来指明要使用的命名空间。

除了直接使用域解析操作符,还可以采用using关键字声明,例如:

  1. using Li::fp;
  2. fp = fopen(“one.txt”, “r”); //使用小李定义的变量 fp
  3. Han :: fp = fopen(“two.txt”, “rb+”); //使用小韩定义的变量 fp

在代码的开头用using声明了Li::fp,它的意思是,using 声明以后的程序中如果出现了未指明命名空间的 fp,就使用 Li::fp;但是若要使用小韩定义的 fp,仍然需要 Han::fp。

using 声明不仅可以针对命名空间中的一个变量,也可以用于声明整个命名空间,例如:

  1. using namespace Li;
  2. fp = fopen(“one.txt”, “r”); //使用小李定义的变量 fp
  3. Han::fp = fopen(“two.txt”, “rb+”); //使用小韩定义的变量 fp

如果命名空间 Li 中还定义了其他的变量,那么同样具有 fp 变量的效果。在 using 声明后,如果有未具体指定命名空间的变量产生了命名冲突,那么默认采用命名空间 Li 中的变量。

命名空间内部不仅可以声明或定义变量,对于其它能在命名空间以外声明或定义的名称,同样也都能在命名空间内部进行声明或定义,例如类、函数、typedef、#define 等都可以出现在命名空间中。

站在编译和链接的角度,代码中出现的变量名、函数名、类名等都是一种符号(Symbol)。有的符号可以指代一个内存位置,例如变量名、函数名;有的符号仅仅是一个新的名称,例如 typedef 定义的类型别名。

下面来看一个命名空间完整示例代码:

  1. #include
  2. //将类定义在命名空间中
  3. namespace Diy{
  4. class Student{
  5. public:
  6. char *name;
  7. int age;
  8. float score;
  9. public:
  10. void say(){
  11. printf(“%s的年龄是 %d,成绩是 %f\n”, name, age, score);
  12. }
  13. };
  14. }
  15. int main(){
  16. Diy::Student stu1;
  17. stu1.name = “小明”;
  18. stu1.age = 15;
  19. stu1.score = 92.5f;
  20. stu1.say();
  21. return 0;
  22. }

运行结果:
小明的年龄是 15,成绩是 92.500000