在 C++ 中定义和使用函数

C++C++Beginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

介绍

在本实验中,你将深入探索 C++ 中的函数世界。函数是编程的基石,它允许你将代码组织成可管理、可重用的块。本实验涵盖了广泛的函数相关主题,从创建具有不同返回类型的函数到利用递归和函数重载等高级概念。你将学习如何定义函数、按值和按引用传递参数、设置默认参数值,并理解函数原型。此外,你还将了解如何使用结构体返回多个值。通过本实验的学习,你将为编写模块化、高效且结构良好的 C++ 代码打下坚实的基础。

我们将从探索具有不同返回类型的函数开始,例如整数、浮点数、字符和字符串。然后,你将学习按值传递参数和按引用传递参数之间的关键区别,以及默认参数值如何为你的函数增加灵活性。我们还将介绍函数重载,它允许你定义多个具有相同名称但参数列表不同的函数。此外,你将探索递归函数,这是一种通过调用自身来解决问题的强大工具。你将看到如何在头文件中使用函数原型来创建更有组织的代码。最后,你将了解如何使用结构体从函数中返回多个值。

创建具有不同返回类型的函数

函数是编程中的基础概念。它们允许你将大型程序分解为更小、更易管理的部分,从而使代码更易于编写、理解和维护。函数还可以返回值,使数据能够传递回程序的调用部分。

以下是 C++ 中函数的基本语法:

return_type function_name(parameters) {
    // 函数体
    return value;
}
  • return_type:指定函数将返回的值的数据类型。如果函数不返回任何值,则使用关键字 void
  • function_name:这是你用来调用函数的名称。选择一个能清晰描述函数用途的名称。
  • parameters:这些是传递给函数的输入值。它们是可选的,可以有零个或多个参数。
  • value:这是函数返回的值。它应与函数的 return_type 匹配。如果 return_typevoid,则可以省略 return 语句,或者仅使用 return; 而不带值。

在这一步中,你将学习创建返回不同数据类型的函数。这种能力对于构建多功能和实用的程序至关重要。

打开 WebIDE,在 ~/project 目录下创建一个名为 function_types.cpp 的新文件:

touch ~/project/function_types.cpp

在编辑器中打开 function_types.cpp 文件,并添加以下代码:

#include <iostream>
#include <string>

// 返回整数的函数
int addNumbers(int a, int b) {
    return a + b;
}

// 返回双精度浮点数的函数
double calculateAverage(double x, double y) {
    return (x + y) / 2.0;
}

// 返回字符的函数
char getGrade(int score) {
    if (score >= 90) return 'A';
    if (score >= 80) return 'B';
    if (score >= 70) return 'C';
    if (score >= 60) return 'D';
    return 'F';
}

// 返回字符串的函数
std::string getStudentStatus(bool isEnrolled) {
    return isEnrolled ? "Active" : "Inactive";
}

int main() {
    // 演示不同的返回类型
    int sum = addNumbers(5, 3);
    std::cout << "Sum: " << sum << std::endl;

    double average = calculateAverage(10.5, 20.5);
    std::cout << "Average: " << average << std::endl;

    char grade = getGrade(85);
    std::cout << "Grade: " << grade << std::endl;

    std::string status = getStudentStatus(true);
    std::cout << "Student Status: " << status << std::endl;

    return 0;
}

编译并运行程序:

g++ function_types.cpp -o function_types
./function_types

示例输出:

Sum: 8
Average: 15.5
Grade: B
Student Status: Active

关于函数返回类型的关键点:

  • 函数可以返回不同的数据类型,为代码提供灵活性。
  • return_type 在函数名称之前指定,指示函数将返回的数据类型。
  • return 语句必须与声明的 return_type 匹配,否则编译器将报告错误。
  • void 函数不返回值,如果你在 void 函数的末尾,可以使用 return; 或直接省略返回语句。
  • 你可以返回基本类型(如 intdoublechar)、字符串,甚至用户定义的类型。
  • 将函数返回类型视为不同类型的容器。就像你会使用盒子、袋子或篮子来携带不同的物品一样,函数使用不同的返回类型来返回不同类型的数据。

按值传递参数给函数

在这一步中,你将学习如何在 C++ 中按值传递参数给函数。当你按值传递参数时,会创建原始参数的副本,并在函数内部使用该副本。在函数内部对参数所做的任何更改都不会影响函数外部的原始变量。

打开 WebIDE,在 ~/project 目录下创建一个名为 pass_by_value.cpp 的新文件:

touch ~/project/pass_by_value.cpp

在编辑器中打开 pass_by_value.cpp 文件,并添加以下代码:

#include <iostream>

// 演示按值传递的函数
void modifyValue(int x) {
    // 此修改仅影响局部副本,不会影响原始变量
    x = x * 2;
    std::cout << "函数内部 - x 的值: " << x << std::endl;
}

int main() {
    // 原始变量
    int number = 10;

    // 打印原始值
    std::cout << "函数调用前 - 原始值: " << number << std::endl;

    // 使用原始变量调用函数
    modifyValue(number);

    // 原始变量保持不变
    std::cout << "函数调用后 - 原始值: " << number << std::endl;

    return 0;
}

编译并运行程序:

g++ pass_by_value.cpp -o pass_by_value
./pass_by_value

示例输出:

函数调用前 - 原始值: 10
函数内部 - x 的值: 20
函数调用后 - 原始值: 10

让我们创建另一个示例来进一步说明按值传递:

touch ~/project/swap_values.cpp

将以下代码添加到 swap_values.cpp 中:

#include <iostream>
#include <string>

// 交换值的函数(按值传递时不会按预期工作)
void swapValues(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
    std::cout << "函数内部 - a: " << a << ", b: " << b << std::endl;
}

int main() {
    int first = 5;
    int second = 10;

    std::cout << "交换前 - first: " << first << ", second: " << second << std::endl;

    // 调用交换函数
    swapValues(first, second);

    // 原始变量保持不变
    std::cout << "交换后 - first: " << first << ", second: " << second << std::endl;

    return 0;
}

编译并运行第二个程序:

g++ swap_values.cpp -o swap_values
./swap_values

示例输出:

交换前 - first: 5, second: 10
函数内部 - a: 10, b: 5
交换后 - first: 5, second: 10

关于按值传递的关键点:

  • 将参数的副本传递给函数,保留原始值。
  • 在函数内部对参数所做的更改不会影响函数外部的原始变量。
  • 按值传递适用于简单数据类型(如 intcharfloat 等),当你不需要修改原始值时。
  • 当按值传递大型对象时,复制可能会消耗更多内存和时间,使其效率低于按引用传递。

你可以将按值传递类比为制作文档的复印件。原始文档保持不变,你可以修改复印件而不会影响原始文档。

使用 & 运算符实现按引用传递

在这一步中,你将学习如何在 C++ 中使用 & 运算符按引用传递参数给函数。按引用传递允许函数直接访问原始变量,这意味着在函数内部对参数所做的任何更改都会直接修改函数外部的原始变量。

打开 WebIDE,在 ~/project 目录下创建一个名为 pass_by_reference.cpp 的新文件:

touch ~/project/pass_by_reference.cpp

在编辑器中打开 pass_by_reference.cpp 文件,并添加以下代码:

#include <iostream>

// 演示按引用传递的函数
void swapValues(int& a, int& b) {
    // 直接修改原始变量
    int temp = a;
    a = b;
    b = temp;
}

// 使用引用修改值的函数
void incrementValue(int& x) {
    // 直接增加原始变量
    x++;
}

int main() {
    // 交换示例
    int first = 5;
    int second = 10;

    std::cout << "交换前 - first: " << first << ", second: " << second << std::endl;

    // 使用引用调用交换函数
    swapValues(first, second);

    std::cout << "交换后 - first: " << first << ", second: " << second << std::endl;

    // 增加示例
    int number = 7;

    std::cout << "增加前: " << number << std::endl;

    // 使用引用调用增加函数
    incrementValue(number);

    std::cout << "增加后: " << number << std::endl;

    return 0;
}

编译并运行程序:

g++ pass_by_reference.cpp -o pass_by_reference
./pass_by_reference

示例输出:

交换前 - first: 5, second: 10
交换后 - first: 10, second: 5
增加前: 7
增加后: 8

让我们创建另一个示例来展示按引用传递的强大功能:

touch ~/project/string_reference.cpp

将以下代码添加到 string_reference.cpp 中:

#include <iostream>
#include <string>

// 使用引用修改字符串的函数
void appendText(std::string& text) {
    text += " - Modified";
}

int main() {
    std::string message = "Hello, World!";

    std::cout << "原始消息: " << message << std::endl;

    // 使用引用直接修改字符串
    appendText(message);

    std::cout << "修改后的消息: " << message << std::endl;

    return 0;
}

编译并运行第二个程序:

g++ string_reference.cpp -o string_reference
./string_reference

示例输出:

原始消息: Hello, World!
修改后的消息: Hello, World! - Modified

关于按引用传递的关键点:

  • 在类型后使用 & 创建引用参数(例如 int& a)。
  • 直接修改原始变量,与按值传递使用副本不同。
  • 避免了复制大型对象的开销,使其在处理复杂数据类型时更高效。
  • 当你需要修改原始值或在不复制的情况下处理复杂数据类型时非常有用。

你可以将按引用传递类比为将原始文档交给某人,而不是复印件。他们所做的任何更改都会直接影响原始文档。

为函数参数设置默认值

在这一步中,你将学习如何在 C++ 中为函数参数设置默认值。默认参数允许你为函数参数分配默认值,从而使你的函数更加灵活且易于使用。当调用者省略具有默认值的参数时,将自动使用默认值。

打开 WebIDE,在 ~/project 目录下创建一个名为 default_parameters.cpp 的新文件:

touch ~/project/default_parameters.cpp

在编辑器中打开 default_parameters.cpp 文件,并添加以下代码:

#include <iostream>
#include <string>

// 带有默认参数的函数
void greetUser(std::string name = "Guest", int age = 0) {
    std::cout << "Hello, " << name << "!" << std::endl;

    if (age > 0) {
        std::cout << "You are " << age << " years old." << std::endl;
    }
}

// 另一个带有默认参数的计算示例
double calculateArea(double length = 1.0, double width = 1.0) {
    return length * width;
}

int main() {
    // 调用函数时不传递参数(使用默认值)
    greetUser();

    // 调用函数时传递部分参数
    greetUser("Alice");

    // 调用函数时传递所有参数
    greetUser("Bob", 25);

    // 演示使用默认参数进行面积计算
    std::cout << "\n面积计算:" << std::endl;

    // 默认面积(1x1)
    std::cout << "默认面积: " << calculateArea() << std::endl;

    // 传递一个参数的面积
    std::cout << "长度为 5 的面积: " << calculateArea(5.0) << std::endl;

    // 传递两个参数的面积
    std::cout << "长度为 5 且宽度为 3 的面积: " << calculateArea(5.0, 3.0) << std::endl;

    return 0;
}

编译并运行程序:

g++ default_parameters.cpp -o default_parameters
./default_parameters

示例输出:

Hello, Guest!
Hello, Alice!
Hello, Bob!
You are 25 years old.

面积计算:
默认面积: 1
长度为 5 的面积: 5
长度为 5 且宽度为 3 的面积: 15

关于默认参数的关键点:

  • 默认值在函数声明中指定,紧跟在参数类型之后(例如 int age = 0)。
  • 具有默认值的参数必须位于参数列表的末尾。不能在默认参数之后放置非默认参数。
  • 你可以使用较少的参数调用函数,缺失的参数将使用其默认值。
  • 默认值提供了灵活性,并减少了对具有略微不同输入但功能相同的多个函数重载的需求。

让我们创建另一个示例来演示更复杂的默认参数:

touch ~/project/student_info.cpp

将以下代码添加到 student_info.cpp 中:

#include <iostream>
#include <string>

// 带有多个默认参数的函数
void printStudentInfo(
    std::string name = "Unknown",
    int age = 0,
    std::string grade = "N/A",
    bool isEnrolled = false
) {
    std::cout << "Name: " << name << std::endl;
    std::cout << "Age: " << age << std::endl;
    std::cout << "Grade: " << grade << std::endl;
    std::cout << "Enrollment Status: " << (isEnrolled ? "Enrolled" : "Not Enrolled") << std::endl;
}

int main() {
    // 调用函数的不同方式
    printStudentInfo();  // 全部使用默认值
    std::cout << "\n";
    printStudentInfo("John");  // 仅传递姓名
    std::cout << "\n";
    printStudentInfo("Alice", 20);  // 传递姓名和年龄
    std::cout << "\n";
    printStudentInfo("Bob", 22, "A", true);  // 传递所有参数

    return 0;
}

编译并运行第二个程序:

g++ student_info.cpp -o student_info
./student_info

示例输出:

Name: Unknown
Age: 0
Grade: N/A
Enrollment Status: Not Enrolled

Name: John
Age: 0
Grade: N/A
Enrollment Status: Not Enrolled

Name: Alice
Age: 20
Grade: N/A
Enrollment Status: Not Enrolled

Name: Bob
Age: 22
Grade: A
Enrollment Status: Enrolled

你可以将默认参数类比为自助餐,你可以选择拿取所有菜品或仅拿取部分菜品,如果你未指定某些菜品,则会自动为你填充。

创建具有不同参数的函数重载

在这一步中,你将学习 C++ 中的函数重载功能,该功能允许你创建多个具有相同名称的函数,只要它们的参数列表不同(参数数量或参数类型不同)。通过使用相同的函数名称对不同数据类型或不同数量的输入执行类似操作,此功能使你的代码更加灵活和易读。

打开 WebIDE,在 ~/project 目录下创建一个名为 function_overloads.cpp 的新文件:

touch ~/project/function_overloads.cpp

在编辑器中打开 function_overloads.cpp 文件,并添加以下代码:

#include <iostream>
#include <string>

// 用于添加不同类型数字的函数重载
int add(int a, int b) {
    std::cout << "Adding two integers" << std::endl;
    return a + b;
}

double add(double a, double b) {
    std::cout << "Adding two doubles" << std::endl;
    return a + b;
}

// 具有不同参数数量的函数重载
int add(int a, int b, int c) {
    std::cout << "Adding three integers" << std::endl;
    return a + b + c;
}

// 具有不同参数类型的函数重载
std::string add(std::string a, std::string b) {
    std::cout << "Concatenating strings" << std::endl;
    return a + b;
}

int main() {
    // 调用不同的重载函数
    std::cout << "Integer addition: " << add(5, 3) << std::endl;
    std::cout << "Double addition: " << add(5.5, 3.7) << std::endl;
    std::cout << "Three integer addition: " << add(1, 2, 3) << std::endl;
    std::cout << "String concatenation: " << add("Hello, ", "World!") << std::endl;

    return 0;
}

编译并运行程序:

g++ function_overloads.cpp -o function_overloads
./function_overloads

示例输出:

Adding two integers
Integer addition: 8
Adding two doubles
Double addition: 9.2
Adding three integers
Three integer addition: 6
Concatenating strings
String concatenation: Hello, World!

让我们创建另一个示例来演示更多的函数重载:

touch ~/project/overloading_examples.cpp

将以下代码添加到 overloading_examples.cpp 中:

#include <iostream>
#include <string>

// 重载的打印函数
void print(int value) {
    std::cout << "Printing integer: " << value << std::endl;
}

void print(double value) {
    std::cout << "Printing double: " << value << std::endl;
}

void print(std::string value) {
    std::cout << "Printing string: " << value << std::endl;
}

// 重载的计算函数
int calculate(int a, int b) {
    return a + b;
}

double calculate(double a, double b) {
    return a * b;
}

int main() {
    // 演示函数重载
    print(42);
    print(3.14);
    print("Hello, Overloading!");

    std::cout << "Integer calculation: " << calculate(5, 3) << std::endl;
    std::cout << "Double calculation: " << calculate(5.5, 3.2) << std::endl;

    return 0;
}

编译并运行第二个程序:

g++ overloading_examples.cpp -o overloading_examples
./overloading_examples

示例输出:

Printing integer: 42
Printing double: 3.14
Printing string: Hello, Overloading!
Integer calculation: 8
Double calculation: 17.6

关于函数重载的关键点:

  • 只要函数具有不同的参数列表(参数类型或参数数量不同),它们就可以具有相同的名称。
  • C++ 编译器根据函数调用期间提供的参数类型和数量决定调用哪个重载函数。
  • 函数重载使代码更具可读性和直观性,允许你为类似的操作重用名称。
  • 如果函数仅因返回类型不同而不同,则无法重载。参数必须不同才能使重载正常工作。

你可以将函数重载类比为适用于不同类型设备的通用遥控器。相同的按钮(函数名称)根据上下文(你提供的参数)执行不同的操作。

编写递归函数计算阶乘

在这一步中,你将通过实现一个阶乘计算程序来学习递归函数。递归是一种编程技术,其中函数通过调用自身来解决问题。为了避免无限循环,递归函数必须有一个基本情况(base case),用于停止递归调用,同时还需要一个递归情况(recursive case),以便继续调用自身,逐步接近基本情况。

打开 WebIDE,在 ~/project 目录下创建一个名为 factorial_recursive.cpp 的新文件:

touch ~/project/factorial_recursive.cpp

在编辑器中打开 factorial_recursive.cpp 文件,并添加以下代码:

#include <iostream>

// 递归函数计算阶乘
unsigned long long calculateFactorial(int n) {
    // 基本情况:0 或 1 的阶乘为 1
    if (n == 0 || n == 1) {
        return 1;
    }

    // 递归情况:n! = n * (n-1)!
    return n * calculateFactorial(n - 1);
}

int main() {
    // 计算不同数字的阶乘
    int numbers[] = {0, 1, 5, 7, 10};

    for (int num : numbers) {
        unsigned long long result = calculateFactorial(num);
        std::cout << "Factorial of " << num << " is: " << result << std::endl;
    }

    return 0;
}

编译并运行程序:

g++ factorial_recursive.cpp -o factorial_recursive
./factorial_recursive

示例输出:

Factorial of 0 is: 1
Factorial of 1 is: 1
Factorial of 5 is: 120
Factorial of 7 is: 5040
Factorial of 10 is: 3628800

让我们创建另一个示例,展示更详细的递归函数:

touch ~/project/factorial_steps.cpp

将以下代码添加到 factorial_steps.cpp 中:

#include <iostream>

// 带有逐步输出的递归函数
unsigned long long calculateFactorialWithSteps(int n, int depth = 0) {
    // 添加缩进以便可视化
    std::string indent(depth * 2, ' ');

    // 基本情况:0 或 1 的阶乘为 1
    if (n == 0 || n == 1) {
        std::cout << indent << "Base case: factorial(" << n << ") = 1" << std::endl;
        return 1;
    }

    // 递归情况并可视化
    std::cout << indent << "Calculating factorial(" << n << ")" << std::endl;

    // 递归调用
    unsigned long long subResult = calculateFactorialWithSteps(n - 1, depth + 1);

    // 组合结果
    unsigned long long result = n * subResult;

    std::cout << indent << "factorial(" << n << ") = "
              << n << " * factorial(" << n-1 << ") = "
              << result << std::endl;

    return result;
}

int main() {
    int number = 5;

    std::cout << "Factorial calculation steps for " << number << ":" << std::endl;
    unsigned long long result = calculateFactorialWithSteps(number);

    std::cout << "\nFinal result: " << number << "! = " << result << std::endl;

    return 0;
}

编译并运行第二个程序:

g++ factorial_steps.cpp -o factorial_steps
./factorial_steps

示例输出:

Factorial calculation steps for 5:
Calculating factorial(5)
  Calculating factorial(4)
    Calculating factorial(3)
      Calculating factorial(2)
        Calculating factorial(1)
        Base case: factorial(1) = 1
      factorial(2) = 2 * factorial(1) = 2
    factorial(3) = 3 * factorial(2) = 6
  factorial(4) = 4 * factorial(3) = 24
factorial(5) = 5 * factorial(4) = 120

Final result: 5! = 120

关于递归函数的关键点:

  • 递归函数调用自身: 函数通过调用自身来解决相同类型的较小子问题。
  • 基本情况(Base Case): 递归函数必须有一个基本情况,用于停止递归调用。如果没有基本情况,函数将无限调用自身,导致栈溢出错误。
  • 递归情况(Recursive Case): 递归情况是函数调用自身以逐步接近基本情况的部分。
  • 问题分解: 递归将复杂问题分解为更小、更简单的子问题,使其更易于管理。
  • 栈的使用: 每次递归调用都会占用程序调用栈的空间。深度递归可能导致栈溢出,因此需要注意调用栈的限制。
  • 适用问题: 递归特别适合那些可以自然地定义为较小、相似子问题的问题,例如树遍历、分治算法和分形生成。

你可以将递归类比为一组嵌套的俄罗斯套娃,每个套娃都包含一个更小的自身版本,而最内层的套娃代表停止嵌套过程的基本情况。

在头文件中使用函数原型

在这一步中,你将学习函数原型以及如何使用头文件在 C++ 中组织和声明函数。函数原型声明函数的名称、返回类型和参数,但不提供实现(函数的主体)。通过使用头文件,你可以将函数的接口(其声明)与其实现分离开来。这种分离提高了代码的组织性,使得在程序的多个部分或多个程序中维护和重用函数变得更加容易。

打开 WebIDE,在 ~/project 目录下创建三个文件:

首先,创建 math_functions.h

touch ~/project/math_functions.h

将以下代码添加到 math_functions.h 中:

#ifndef MATH_FUNCTIONS_H
#define MATH_FUNCTIONS_H

// 数学操作的函数原型
int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
double divide(double a, double b);

#endif // MATH_FUNCTIONS_H

.h 文件用于声明,包含函数原型和其他声明,但不包含实现。这样,你可以在不实现函数的情况下声明它们。#ifndef#define#endif 指令称为包含保护(include guards),它们防止头文件被多次包含,从而避免错误。

接下来,创建 math_functions.cpp

touch ~/project/math_functions.cpp

将以下代码添加到 math_functions.cpp 中:

#include "math_functions.h"

// 函数实现
int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}

int multiply(int a, int b) {
    return a * b;
}

double divide(double a, double b) {
    // 检查除以零的情况
    if (b == 0) {
        return 0;
    }
    return a / b;
}

这个 .cpp 文件包含了头文件中声明的函数的实际实现。

最后,创建 main.cpp

touch ~/project/main.cpp

将以下代码添加到 main.cpp 中:

#include <iostream>
#include "math_functions.h"

int main() {
    // 演示从头文件中调用的函数
    int x = 10, y = 5;

    std::cout << "Addition: " << x << " + " << y << " = " << add(x, y) << std::endl;
    std::cout << "Subtraction: " << x << " - " << y << " = " << subtract(x, y) << std::endl;
    std::cout << "Multiplication: " << x << " * " << y << " = " << multiply(x, y) << std::endl;
    std::cout << "Division: " << x << " / " << y << " = " << divide(x, y) << std::endl;

    return 0;
}

这个 main.cpp 文件包含了 math_functions.h 头文件,使得函数原型可用。然后它可以使用 math_functions.cpp 中实现的函数。

使用多个源文件编译程序:

g++ math_functions.cpp main.cpp -o math_operations
./math_operations

示例输出:

Addition: 10 + 5 = 15
Subtraction: 10 - 5 = 5
Multiplication: 10 * 5 = 50
Division: 10 / 5 = 2

让我们创建另一个示例,展示更复杂的头文件:

创建 calculator.h

touch ~/project/calculator.h

将以下代码添加到 calculator.h 中:

#ifndef CALCULATOR_H
#define CALCULATOR_H

class Calculator {
public:
    // 计算器操作的函数原型
    int add(int a, int b);
    int subtract(int a, int b);
    int calculate(int a, int b, char operation);
};

#endif // CALCULATOR_H

这个头文件声明了一个名为 Calculator 的类及其公共方法。

创建 calculator.cpp

touch ~/project/calculator.cpp

将以下代码添加到 calculator.cpp 中:

#include "calculator.h"

int Calculator::add(int a, int b) {
    return a + b;
}

int Calculator::subtract(int a, int b) {
    return a - b;
}

int Calculator::calculate(int a, int b, char operation) {
    switch (operation) {
        case '+': return add(a, b);
        case '-': return subtract(a, b);
        default: return 0;
    }
}

这个 calculator.cpp 提供了 calculator.h 头文件中声明的方法的实现。

创建 calculator_main.cpp

touch ~/project/calculator_main.cpp

将以下代码添加到 calculator_main.cpp 中:

#include <iostream>
#include "calculator.h"

int main() {
    Calculator calc;

    std::cout << "Calculator Operations:" << std::endl;
    std::cout << "10 + 5 = " << calc.calculate(10, 5, '+') << std::endl;
    std::cout << "10 - 5 = " << calc.calculate(10, 5, '-') << std::endl;

    return 0;
}

这个主文件使用 Calculator 类并执行操作。

编译计算器程序:

g++ calculator.cpp calculator_main.cpp -o calculator
./calculator

示例输出:

Calculator Operations:
10 + 5 = 15
10 - 5 = 5

关于函数原型和头文件的关键点:

  • 头文件 (.h) 声明函数原型、类和其他声明。它们充当接口,概述了可用的函数。
  • 源文件 (.cpp) 实现头文件中声明的实际函数。它们包含函数如何工作的代码。
  • #ifndef#define#endif(包含保护)防止同一头文件被多次包含,从而避免潜在的编译错误。
  • 使用头文件促进了模块化和代码的可重用性。
  • 头文件允许你将“什么”(声明)与“如何”(实现)分离开来。
  • 它们有助于代码组织,使其更易于维护和理解。

你可以将头文件类比为餐厅菜单。菜单(头文件)列出了可用的内容,而厨房(源文件)则准备实际的菜肴。

使用结构体返回多个值

在这一步中,你将学习如何使用结构体从 C++ 函数中返回多个值。结构体是用户定义的数据类型,允许你将不同类型的数据组合在一个名称下。它们非常适合以结构化和组织化的方式从函数中返回多个相关的值。

打开 WebIDE,更新 ~/project 目录下的 student_info.cpp 文件:

将现有代码替换为以下内容:

#include <iostream>
#include <string>

// 定义一个结构体来保存学生信息
struct StudentResult {
    std::string name;
    int score;
    bool passed;
};

// 使用结构体返回多个值的函数
StudentResult evaluateStudent(std::string name, int testScore) {
    StudentResult result;
    result.name = name;
    result.score = testScore;
    result.passed = (testScore >= 60);

    return result;
}

// 计算多个统计值的函数
struct MathStats {
    int sum;
    double average;
    int minimum;
    int maximum;
};

MathStats calculateArrayStats(int numbers[], int size) {
    MathStats stats;
    stats.sum = 0;
    stats.minimum = numbers[0];
    stats.maximum = numbers[0];

    for (int i = 0; i < size; i++) {
        stats.sum += numbers[i];

        if (numbers[i] < stats.minimum) {
            stats.minimum = numbers[i];
        }

        if (numbers[i] > stats.maximum) {
            stats.maximum = numbers[i];
        }
    }

    stats.average = static_cast<double>(stats.sum) / size;

    return stats;
}

int main() {
    // 演示学生评估
    StudentResult student1 = evaluateStudent("Alice", 75);
    StudentResult student2 = evaluateStudent("Bob", 45);

    std::cout << "Student Results:" << std::endl;
    std::cout << student1.name << " - Score: " << student1.score
              << ", Passed: " << (student1.passed ? "Yes" : "No") << std::endl;
    std::cout << student2.name << " - Score: " << student2.score
              << ", Passed: " << (student2.passed ? "Yes" : "No") << std::endl;

    // 演示数组统计
    int numbers[] = {5, 2, 9, 1, 7};
    int size = sizeof(numbers) / sizeof(numbers[0]);

    MathStats stats = calculateArrayStats(numbers, size);

    std::cout << "\nArray Statistics:" << std::endl;
    std::cout << "Sum: " << stats.sum << std::endl;
    std::cout << "Average: " << stats.average << std::endl;
    std::cout << "Minimum: " << stats.minimum << std::endl;
    std::cout << "Maximum: " << stats.maximum << std::endl;

    return 0;
}

编译并运行程序:

g++ student_info.cpp -o student_info
./student_info

示例输出:

Student Results:
Alice - Score: 75, Passed: Yes
Bob - Score: 45, Passed: No

Array Statistics:
Sum: 24
Average: 4.8
Minimum: 1
Maximum: 9

关于结构体和返回多个值的关键点:

  • 结构体将相关数据组合在一起: 你可以将不同类型的变量(例如 stringintbool)组合成一个单元,这在处理相关信息时非常有用。
  • 返回复杂数据类型: 使用结构体,你可以从函数中返回复杂的数据类型,从而轻松管理和传递相关的数据集。
  • 组织返回值: 返回结构体有助于组织多个相关的返回值,使代码更清晰且更易于维护。
  • 提供传递结构化信息的清晰方式: 结构体为返回多个相关值提供了一个清晰的、命名的容器,提高了代码的可读性和可理解性。

你可以将结构体类比为一个多格午餐盒,可以在不同的格子中存放不同类型的食物。

总结

在本实验中,你全面了解了如何在 C++ 中定义和使用函数。你学习了如何创建具有不同返回类型的函数、按值和按引用传递参数、设置默认参数值以及利用函数重载。此外,你还探索了递归函数、如何在头文件中使用函数原型,以及如何使用结构体返回多个值。

涵盖的关键概念包括:

  • 函数返回类型: 你了解到函数可以返回不同的数据类型,并且 return 语句必须与声明的返回类型匹配。
  • 按值传递和按引用传递: 你理解了按值传递参数(创建副本)和按引用传递参数(使用原始变量)之间的区别。
  • 默认参数: 你学习了如何通过为函数参数分配默认值来使函数更加灵活。
  • 函数重载: 你看到了如何定义多个具有相同名称但参数列表不同的函数,从而使代码更具可读性和直观性。
  • 递归函数: 你探索了递归函数的强大功能,其中函数通过调用自身来解决较小的子问题,同时了解了基本情况的重要性,以防止无限递归。
  • 函数原型和头文件: 你学习了如何使用头文件来组织函数声明,从而促进模块化和代码的可重用性。
  • 使用结构体返回多个值: 你发现了如何使用结构体从函数中返回多个相关的值,结构体为数据提供了一个有组织的容器。

通过掌握这些概念,你现在能够编写更加模块化、高效且结构良好的 C++ 代码,并为探索更高级的编程技术奠定了坚实的基础。

您可能感兴趣的其他 C++ 教程