Advertisement

《C++ Primer》导学系列:第 6 章 - 函数

阅读量:

6.1 函数基础

6.1.1 基本概念

在C++程序中,函数被视为核心组件。其主要功能是将分散的代码整合为可重用的模块。通过名称标识的方式调用,并支持传递参数以及返回结果。每个函数由说明部分和实现部分两部分构成,在这种架构下完成特定的功能需求。其中说明部分描述了接口信息(Function Head),而实现部分则包含了具体的代码逻辑(Function Body)。

函数的定义

在编程语言中,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况下,默认情况

示例代码
复制代码
 #include <iostream>

    
  
    
 // 函数定义
    
 int add(int a, int b) {
    
     return a + b;  // 返回 a 和 b 的和
    
 }
    
  
    
 int main() {
    
     int result = add(3, 4);  // 调用 add 函数
    
     std::cout << "Result: " << result << std::endl;  // 输出结果
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/zC2AORnDi8BfgmjlN3TJKEhy96kZ.png)

在以下示例中,请注意:add函数接收两个整数参数,并计算出它们的和。随后,在主程序中(即 main 函数),会先调用该加法操作,并将计算结果输出。

6.1.2 函数声明

前须声明函数之前。
关于编译器所需信息的说明指出包括名称、返回类型和参数类型等信息,并非完整实现。
这些说明一般包含在头文件内,并非完整实现的地方由定义部分完成。

示例代码
复制代码
 #include <iostream>

    
  
    
 // 函数声明
    
 int add(int a, int b);
    
  
    
 int main() {
    
     int result = add(3, 4);  // 调用 add 函数
    
     std::cout << "Result: " << result << std::endl;  // 输出结果
    
     return 0;
    
 }
    
  
    
 // 函数定义
    
 int add(int a, int b) {
    
     return a + b;  // 返回 a 和 b 的和
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/3M2Lsbt9whlN7XojO8TJefm5RgCS.png)

在这个示例中,add函数的声明在main函数之前,定义在main函数之后。

6.1.3 形参和实参

该函数通过形参(形式参数)的方式,在其定义阶段明确指定了参数的具体类型和名称;而当该函数被调用时,则通过实参(实际参数)的方式传递相应的具体数值。值得注意的是,在这一过程中:

  1. 形参与实参与其各自的作用位置均得到了明确的规定
  2. 实参与其提供的具体数值是该过程得以顺利运行的关键要素
  3. 这种机制确保了数据能够准确地从外部环境传递到函数的核心逻辑中
  4. 从而保证了整个程序运行的有效性和可靠性
示例代码
复制代码
 #include <iostream>

    
  
    
 // 函数定义
    
 void printSum(int a, int b) {
    
     std::cout << "Sum: " << a + b << std::endl;  // 输出 a 和 b 的和
    
 }
    
  
    
 int main() {
    
     int x = 5;
    
     int y = 7;
    
     printSum(x, y);  // 调用 printSum 函数,x 和 y 是实参
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/FUB3lgN1mz7fMrOK80wvutns9oqy.png)

在这个示例中,在调用时传入两个整数参数a和b,并传递指定的数值参数x和y。

6.1.4 返回类型

该函数能够返回一个值;其 return 类型可包括任意基本数据类型的指针、引用或对象指针等;若该函数无 return 值,则其 return 类型定义为 void

示例代码
复制代码
 #include <iostream>

    
  
    
 // 函数定义,返回类型为 int
    
 int multiply(int a, int b) {
    
     return a * b;  // 返回 a 和 b 的乘积
    
 }
    
  
    
 // 函数定义,返回类型为 void
    
 void printMessage() {
    
     std::cout << "Hello, World!" << std::endl;
    
 }
    
  
    
 int main() {
    
     int result = multiply(3, 4);  // 调用 multiply 函数
    
     std::cout << "Result: " << result << std::endl;  // 输出结果
    
  
    
     printMessage();  // 调用 printMessage 函数
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/yOZxYiEGbnfJ9swl6uCde2KRWMvN.png)

在以下示例中,在线计算工具中的multiply函数用于计算两个整数的积,在线计算工具中的printMessage函数则用于输出相关信息而不输出任何内容。

6.1.5 函数调用

当进行函数调用时, 实参值将被传递给形参, 并在完成后执行相应操作

示例代码
复制代码
 #include <iostream>

    
  
    
 // 函数定义
    
 int subtract(int a, int b) {
    
     return a - b;  // 返回 a 和 b 的差
    
 }
    
  
    
 int main() {
    
     int a = 10;
    
     int b = 3;
    
     int result = subtract(a, b);  // 调用 subtract 函数
    
     std::cout << "Result: " << result << std::endl;  // 输出结果
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/vFLM4x1kbuN0nmBo29Zrt68pgdWq.png)

在以下示例中,在此案例中

重点与难点分析

重点

  1. 函数定义与声明 :了解函数的定义与声明机制,在代码开发中熟练运用相关技术。
  2. 形参和实参 :深入理解其概念及其在程序设计中的实际应用。
  3. 返回类型 :学习如何确定返回类型的方法,并掌握不同场景下的应用规范。

难点

  1. 函数调用过程:学习者应在实际操作中掌握函数调用的基本规则,并准确理解实参与形参之间的对应关系。 2. 应用返回类型:在处理复杂程序时,应科学地设计并合理利用返回类型,并特别关注以指针和引用类型为返回值的情况。

练习题解析

练习6.1 :编写一个函数,接受一个整数参数并打印该整数的平方。

    • 示例代码
复制代码
 #include <iostream>

    
  
    
 void printSquare(int x) {
    
     std::cout << "Square: " << x * x << std::endl;
    
 }
    
  
    
 int main() {
    
     printSquare(4);
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/RjMif7KzuaT9hFYByDHOrW3PbSLp.png)

练习6.2 :编写一个函数,接受一个字符参数并返回该字符的大写形式。

    • 示例代码
复制代码
 #include <iostream>

    
 #include <cctype>
    
  
    
 char toUpperCase(char c) {
    
     return std::toupper(c);
    
 }
    
  
    
 int main() {
    
     char c = 'a';
    
     char result = toUpperCase(c);
    
     std::cout << "Uppercase: " << result << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/HOQEU1u6ajiVTf8kxFeWJX0ADgnt.png)

练习6.3 :编写一个void函数,不接受任何参数,并打印一条问候消息。

    • 示例代码
复制代码
 #include <iostream>

    
  
    
 void greet() {
    
     std::cout << "Hello, welcome to C++ programming!" << std::endl;
    
 }
    
  
    
 int main() {
    
     greet();
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/cGbBpCTioWjXO4VFEvAHfM3rdxP0.png)

总结与提高

本节总结

  1. 深入理解了函数的基本概念,并系统地学习了函数的定义、声明以及调用方法。
  2. 透彻理解了形参与实参的作用机制,并深入掌握了返回类型及其应用。
  3. 结合具体的示例代码分析与适量的练习题训练,进一步加深了对函数基础知识的理解与应用能力。

提高建议

  1. 加强函数定义与调用的基本功训练:通过精心编撰包含多个功能模块的应用程序来加深对函数定义、声明与调用方法的理解与掌握。
  2. 透彻掌握形参与实参的关系:通过实践操作熟悉形参与实参之间的对应关系,并确保在实际应用中能够正确传递参数。
  3. 科学规划函数返回类型安排:在开发较为复杂的系统软件时,在详细需求分析的基础上科学规划好各个子系统的接口设计与实现方案。
  4. 深入理解形参和实参:通过实践掌握形参和实参的对应关系,确保在编程过程中能够正确地传递参数信息。
  5. 合理设计函数的返回类型:在编写复杂程序时,合理设计并使用合适的数据类型,提高代码的整体可读性和维护性

6.2 参数传递

6.2.1 传值参数

按值传递是指将实参的具体值进行复制后传送给函数形参的过程。当函数对形参进行修改时,这些变化不会影响到实参原有的数值。采用按值传递的方式最适合用于传输基本数据类型(如int、double等)以及规模较小的结构体或简单的类对象实例。

示例代码
复制代码
 #include <iostream>

    
  
    
 void increment(int x) {
    
     ++x;  // 仅修改形参 x 的值
    
     std::cout << "Inside function: " << x << std::endl;
    
 }
    
  
    
 int main() {
    
     int a = 5;
    
     increment(a);  // 按值传递,实参 a 的值不会改变
    
     std::cout << "Outside function: " << a << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/TgrYOHUZxK2sFz3bepP5lXBfiVch.png)

在这个示例中,increment函数按值传递参数x,修改形参不会影响实参a

6.2.2 传引用参数

按引用传递即为将实参的引用传输给函数的形参变量,在这种情况下函数对形参的数据进行修改会直接导致实参发生相应变化。按引用传递通常用于需要在函数内部对实参值进行修改的情形,并且特别适用于传递较大的对象结构以避免因不必要的内存操作而产生的开销问题。

示例代码
复制代码
 #include <iostream>

    
  
    
 void increment(int &x) {
    
     ++x;  // 修改形参 x 的值,会影响实参
    
     std::cout << "Inside function: " << x << std::endl;
    
 }
    
  
    
 int main() {
    
     int a = 5;
    
     increment(a);  // 按引用传递,实参 a 的值会改变
    
     std::cout << "Outside function: " << a << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/QX9qRnMY5kr0Com7x6S2FD8vc4bK.png)

在这个示例中,increment函数按引用传递参数x,修改形参会影响实参a

6.2.3 const形参和实参

为了避免函数修改传递的实参,在传递过程中可以采用const reference作为实参传递的方式。这种做法不仅避免了按值传递时的数据拷贝带来的性能开销,还确保了传入参数不会在函数体内发生任何更改。

示例代码
复制代码
 #include <iostream>

    
  
    
 void printValue(const int &x) {
    
     std::cout << "Value: " << x << std::endl;
    
     // 不能修改 x 的值
    
 }
    
  
    
 int main() {
    
     int a = 5;
    
     printValue(a);  // 按常量引用传递,实参 a 的值不会改变
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/go4IJnfLXrabV1edmAv0CY75lhWk.png)

在本例中,在以下示例中

6.2.4 数组形参

在C++编程语言中,当传递给函数作为参数的数组会退化为指向该数组第一个元素的指针。这意味着调用者实际上是在传递该数组内存起始地址而不是整个数据体内容。

示例代码
复制代码
 #include <iostream>

    
  
    
 void printArray(int arr[], int size) {
    
     for (int i = 0; i < size; ++i) {
    
     std::cout << arr[i] << " ";
    
     }
    
     std::cout << std::endl;
    
 }
    
  
    
 int main() {
    
     int myArray[] = {1, 2, 3, 4, 5};
    
     int size = sizeof(myArray) / sizeof(myArray[0]);
    
     printArray(myArray, size);  // 按指针传递数组
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/bsDJwpY3T4iuHxQMLO5ahW9KEN7n.png)

在这个案例中

6.2.5 main函数的形参

main函数支持处理两个参数,并常用于接收命令行输入。其中argc代表输入参数的数量,《argv》则构成一个字符指针数组来存储被传递的具体参数信息。

示例代码
复制代码
 #include <iostream>

    
  
    
 int main(int argc, char *argv[]) {
    
     std::cout << "Number of arguments: " << argc << std::endl;
    
     for (int i = 0; i < argc; ++i) {
    
     std::cout << "Argument " << i << ": " << argv[i] << std::endl;
    
     }
    
     return 0;
    
 }
    
    
    
    
    cpp

在这个示例中,main函数接受命令行参数并打印出来。

6.2.6 含有可变参数的函数

C++支持可变参数模板(variadic templates)以应对参数数量不确定的情况。通过采用省略号(...)作为可变参数列表的标识。

示例代码
复制代码
 #include <iostream>

    
 #include <cstdarg>
    
  
    
 void printValues(const char *fmt, ...) {
    
     va_list args;
    
     va_start(args, fmt);
    
     while (*fmt != '\0') {
    
     switch (*fmt++) {
    
         case 'i': // int
    
             std::cout << va_arg(args, int) << " ";
    
             break;
    
         case 'd': // double
    
             std::cout << va_arg(args, double) << " ";
    
             break;
    
         case 'c': // char
    
             std::cout << static_cast<char>(va_arg(args, int)) << " ";
    
             break;
    
     }
    
     }
    
     va_end(args);
    
     std::cout << std::endl;
    
 }
    
  
    
 int main() {
    
     printValues("icd", 42, 'a', 3.14);
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/FEzxWLNlfBtqj9S5Vad2G1C7Pkch.png)

在这一案例中,在该函数采用变参数列表来处理不同类型的输入以及不同数量的输入。

重点与难点分析

重点

  1. 按值传递与引用传递:深入理解按值传递与引用传递的基本概念及其差异,并掌握它们在实际编程中的应用场景。 2. 常量引用:系统地学习常量引用的应用场景及其优点,并了解其在程序设计中的具体应用。 3. 数组形参:了解数组作为函数参数传递的方式及其实现机制,并掌握其相关的编程技巧。 4. main函数形参:深入理解main函数如何接收并处理命令行参数,并熟悉相关操作流程。 5. 可变参数函数:熟练掌握可变参数函数的定义、实现方式及其在实际编程中的应用场景。

难点

  1. 参数传递路径的选择 :学习者应通过实践来掌握不同传递路径的选择,在具体情境中选择最适合的方式。
    2. 数组转化的影响分析 :探究数组转化为指针所带来的影响及其在功能实现中的作用。
    3. 参数管理细节把控 :确保正确管理各类型参数及其数量,在设计可变参数函数时避免潜在问题。

练习题解析

  1. 练习6.5 :编写一个函数,按值传递一个整数参数,并尝试修改该参数的值。
    • 示例代码
复制代码
 #include <iostream>

    
  
    
 void modifyValue(int x) {
    
     x = 10;
    
     std::cout << "Inside function: " << x << std::endl;
    
 }
    
  
    
 int main() {
    
     int a = 5;
    
     modifyValue(a);  // 按值传递,实参 a 的值不会改变
    
     std::cout << "Outside function: " << a << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/yp38ieJjDob5aCSYWNUnsVfkA2ug.png)
  1. 练习6.6 :编写一个函数,按引用传递一个整数参数,并修改该参数的值。
    • 示例代码
复制代码
 #include <iostream>

    
  
    
 void modifyValue(int &x) {
    
     x = 10;
    
     std::cout << "Inside function: " << x << std::endl;
    
 }
    
  
    
 int main() {
    
     int a = 5;
    
     modifyValue(a);  // 按引用传递,实参 a 的值会改变
    
     std::cout << "Outside function: " << a << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/wlAv6QLDCg9EjFMkGazpVRtUZP3N.png)

练习题6.7:创建一个函数,并通过常量引用传递这个字符串参数,并尝试修改其值

    • 示例代码
复制代码
 #include <iostream>

    
 #include <string>
    
  
    
 void printString(const std::string &str) {
    
     std::cout << "String: " << str << std::endl;
    
     // str = "New String";  // 错误:不能修改常量引用
    
 }
    
  
    
 int main() {
    
     std::string s = "Hello, World!";
    
     printString(s);  // 按常量引用传递,实参 s 的值不会改变
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/Y0LS2T5yIwsvbpquZdPofVCgOrAt.png)
  1. 练习6.8 :编写一个函数,按值传递指针参数,修改指针指向的值。
    • 示例代码
复制代码
 #include <iostream>

    
  
    
 void modifyPointer(int *p) {
    
     *p = 10;
    
     std::cout << "Inside function: " << *p << std::endl;
    
 }
    
  
    
 int main() {
    
     int a = 5;
    
     modifyPointer(&a);  // 按值传递指针,实参 a 的值会改变
    
     std::cout << "Outside function: " << a << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/Vr03W1fEGNmvUaDzKwZ67LhjF5iu.png)
  1. 练习6.9 :编写一个函数,按引用传递指针参数,修改指针的地址。
    • 示例代码
复制代码
 #include <iostream>

    
  
    
 void setPointer(int *&p, int *newAddress) {
    
     p = newAddress;
    
 }
    
  
    
 int main() {
    
     int a = 5;
    
     int b = 10;
    
     int *ptr = &a;
    
     setPointer(ptr, &b);  // 修改 ptr 使其指向 b
    
     std::cout << "Pointer now points to: " << *ptr << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/QahtvAHicxOrJWlZTzq57VMoDf0n.png)
  1. 练习6.10 :编写一个main函数,接受命令行参数并输出这些参数。
    • 示例代码
复制代码
 #include <iostream>

    
  
    
 int main(int argc, char *argv[]) {
    
     std::cout << "Number of arguments: " << argc << std::endl;
    
     for (int i = 0; i < argc; ++i) {
    
     std::cout << "Argument " << i << ": " << argv[i] << std::endl;
    
     }
    
     return 0;
    
 }
    
    
    
    
    cpp

练习6.11 :创建一个能够处理可变数量的参数的函数,并接收多种类型的输入并返回相应的值。

    • 示例代码
复制代码
 #include <iostream>

    
 #include <cstdarg>
    
  
    
 void printValues(const char *fmt, ...) {
    
     va_list args;
    
     va_start(args, fmt);
    
     while (*fmt != '\0') {
    
     switch (*fmt++) {
    
         case 'i': // int
    
             std::cout << va_arg(args, int) << " ";
    
             break;
    
         case 'd': // double
    
             std::cout << va_arg(args, double) << " ";
    
             break;
    
         case 'c': // char
    
             std::cout << static_cast<char>(va_arg(args, int)) << " ";
    
             break;
    
     }
    
     }
    
     va_end(args);
    
     std::cout << std::endl;
    
 }
    
  
    
 int main() {
    
     printValues("icd", 42, 'a', 3.14);
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/25Qz9UbJHwdIuZpFmeV6Ra1isAtE.png)

总结与提高

本节总结

  1. 学习了多种参数传递的方式,包括按值传递、按引用传递、常量引用、数组形参、main函数的形参以及含有可变参数的函数。
  2. 通过具体的示例代码分析和配套练习题,深入理解了不同参数传递方式的应用场景和实现方法。
  3. 掌握了参数传递的关键概念及其应用方法,并能在实际编程中灵活运用这些知识。

提高建议

  1. 加强掌握多种参数传递技巧:通过设计并实现多种程序来实践并深入理解按值、引用以及常量引用的不同机制。
  2. 透彻掌握数组形参与可变参数的概念与应用:通过实际操作加深理解和掌握这些概念,并确保能够正确地将这些机制应用到实际开发中。
  3. 科学选择合适的编程风格与实现细节:针对复杂的编程需求场景中,在编写复杂程序时根据不同场景灵活运用不同的编程策略以达到最佳效果,并从而提升代码的质量。

6.3 返回类型和return语句

6.3.1 返回类型

函数在执行结束后传递给调用者的数据类型即为其实现了它的功能。C++语言允许函数返回基本数据类型以及带有指针或引用类型的变量(如指针或引用)。此外,在C++中还允许函数返回由类实例化所构成的对象(通过构造函数),以及通过数组索引访问内存区域而实现的一种特殊效果(即通过指向数组首地址的方式)。如果一个程序中的某个函数没有任何输出结果,则该函数的功能被定义为虚无型并由void标识。

基本语法
复制代码
 returnType functionName(parameters) {

    
     // 函数体
    
     return value;  // 返回值
    
 }
    
    
    
    
    cpp

6.3.2 返回值的基本类型

函数能够返回基本数据类型(如int和double等)。在函数体中,通过采用return语句向调用者传递值。

示例代码
复制代码
 #include <iostream>

    
  
    
 int add(int a, int b) {
    
     return a + b;  // 返回整数
    
 }
    
  
    
 double multiply(double x, double y) {
    
     return x * y;  // 返回浮点数
    
 }
    
  
    
 int main() {
    
     int sum = add(3, 4);
    
     double product = multiply(2.5, 4.0);
    
  
    
     std::cout << "Sum: " << sum << std::endl;
    
     std::cout << "Product: " << product << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/mnvcsJkAglFIZ3z6KUP90QOWBfVq.png)

在这个示例中,在这种情况下,在这种情形下,在这种情境下,在这种情境下

6.3.3 返回引用类型

该函数可返回引用类型的值。当该函数返回引用实例时,在被调用处接收的实际上是原始对象的引用而非新的对象实例。这对避免复制大型对象以及实现连锁调用非常有帮助。

示例代码
复制代码
 #include <iostream>

    
  
    
 int& getElement(int arr[], int index) {
    
     return arr[index];  // 返回数组元素的引用
    
 }
    
  
    
 int main() {
    
     int myArray[5] = {1, 2, 3, 4, 5};
    
     getElement(myArray, 2) = 10;  // 修改数组第三个元素的值
    
  
    
     for (int i = 0; i < 5; ++i) {
    
     std::cout << myArray[i] << " ";
    
     }
    
     std::cout << std::endl;
    
  
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/hsg1WBKjwUGFf8qxeZdCScRzTuy9.png)

在该示例中,getElement函数提供了数组元素引用,在此基础上使得调用者可以直接修改这些数组元素的值。

6.3.4 返回指针类型

该函数能够返回用于引用的数据地址。当释放或不再需要内存所指向的数据时,请注意维护其有效性以避免潜在的问题。

示例代码
复制代码
 #include <iostream>

    
  
    
 int* findMax(int* a, int* b) {
    
     return (*a > *b) ? a : b;  // 返回指向较大值的指针
    
 }
    
  
    
 int main() {
    
     int x = 10, y = 20;
    
     int* max = findMax(&x, &y);
    
  
    
     std::cout << "Max value: " << *max << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/Zgnlowe4thjDEWsU96iMBq5vcxCL.png)

在这个示例中,findMax函数返回指向较大值的指针。

6.3.5 返回void类型

当函数无需返回值时,则可将其返回类型设定为void。此类函数一般用于完成特定任务而无须输出结果。

示例代码
复制代码
 #include <iostream>

    
  
    
 void printMessage() {
    
     std::cout << "Hello, World!" << std::endl;  // 不返回值
    
 }
    
  
    
 int main() {
    
     printMessage();
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/uCGdPzgxLcpO5XwSQ9R8YHWjrVeF.png)

在这个示例中,printMessage函数不返回值。

重点与难点分析

重点

  1. 在处理函数设计时,涉及多种数据类型的返回策略:包括基础数据类型、引用数据类型的使用、指针数据类型的应用以及空值(void)的处理方式,并结合具体应用背景进行分析。
  2. 深入透彻理解`return$语句的基本功能机制,并熟练掌握它在各种不同返回类型的函数实现中的具体应用技巧。

难点

  1. 引用类型的使用风险 :初学者应理解引用类型的使用风险。
  2. 指针类型的生命周期管理 :需掌握指针类型的生命周期管理。

练习题解析

  1. 练习6.12 :编写一个函数,返回两个整数中的较大值。
    • 示例代码
复制代码
 #include <iostream>

    
  
    
 int max(int a, int b) {
    
     return (a > b) ? a : b;
    
 }
    
  
    
 int main() {
    
     int x = 10, y = 20;
    
     std::cout << "Max: " << max(x, y) << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/VFujsO9M3eUTy6blvmL2E4pHAxfI.png)
  1. 练习6.13 :编写一个函数,返回字符串的引用,并修改该字符串的值。
    • 示例代码
复制代码
 #include <iostream>

    
 #include <string>
    
  
    
 std::string& modifyString(std::string &str) {
    
     str += " modified";
    
     return str;
    
 }
    
  
    
 int main() {
    
     std::string s = "Original";
    
     std::cout << modifyString(s) << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/iPpwaGU4zLnEgoF5Qt9yRqDNcC0K.png)
  1. 练习6.14 :编写一个函数,返回指向数组元素的指针。
    • 示例代码
复制代码
 #include <iostream>

    
  
    
 int* findElement(int arr[], int size, int value) {
    
     for (int i = 0; i < size; ++i) {
    
     if (arr[i] == value) {
    
         return &arr[i];
    
     }
    
     }
    
     return nullptr;
    
 }
    
  
    
 int main() {
    
     int arr[] = {1, 2, 3, 4, 5};
    
     int size = sizeof(arr) / sizeof(arr[0]);
    
     int* elem = findElement(arr, size, 3);
    
  
    
     if (elem) {
    
     std::cout << "Element found: " << *elem << std::endl;
    
     } else {
    
     std::cout << "Element not found." << std::endl;
    
     }
    
  
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/Vx705k6Te3abwKfDAFtj2JpNnd1W.png)
  1. 练习6.15 :编写一个void函数,打印一个消息,并在满足特定条件时提前返回。
    • 示例代码
复制代码
 #include <iostream>

    
  
    
 void checkEven(int x) {
    
     if (x % 2 == 0) {
    
     std::cout << x << " is even." << std::endl;
    
     return;
    
     }
    
     std::cout << x << " is odd." << std::endl;
    
 }
    
  
    
 int main() {
    
     checkEven(10);
    
     checkEven(7);
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/dV13sDPURmJGHSNgtv0j2AM6TbOB.png)

总结与提高

本节总结

  1. 深入学习了关于函数返回类型的知识,并熟练掌握了基本类型、引用类型、指针类型以及 void 类型的具体应用方法。
  2. 弄清楚了 return 语句的功能,并能在各种类型的函数中正确运用它。
  3. 通过编写示例代码并解决相关练习题进一步加深了对函数返回类型及 return 语句的理解与应用。

提高建议

  1. 多练习不同返回类型的函数:通过编写各种包含不同返回类型的函数来练习实现基本类型、引用类型、指针类型和void类型的方法。
  2. 深入理解返回引用和指针的安全性:通过实践掌握返回引用和指针的安全性原则,并避免在函数内部返回局部变量的引用以确保返回的指针在函数执行结束后仍然有效。
  3. 合理使用return语句:在编写代码时合理运用return语句以提高代码的可读性和维护性。

6.4 函数重载

6.4.1 基本概念

在同一个作用域中定义多个具有相同名称但参数列表不同的功能块称为功能重载。其参数列表需在数量或类型上与原功能有所区别以便编译器能够根据调用时提供的实参来选择合适的功能块实现相应的功能这样就能让程序设计更加灵活且易于维护

6.4.2 定义重载函数

重载函数基于定义多个同名但参数列表不同的函数来实现。当编译器使用重载函数时,在接受传递的具体参数类型和数量后选择相应的版本进行调用。

示例代码
复制代码
 #include <iostream>

    
  
    
 // 重载函数定义
    
 void print(int a) {
    
     std::cout << "Integer: " << a << std::endl;
    
 }
    
  
    
 void print(double b) {
    
     std::cout << "Double: " << b << std::endl;
    
 }
    
  
    
 void print(const std::string &c) {
    
     std::cout << "String: " << c << std::endl;
    
 }
    
  
    
 int main() {
    
     print(10);           // 调用第一个 print 函数
    
     print(3.14);         // 调用第二个 print 函数
    
     print("Hello");      // 调用第三个 print 函数
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/nFxM9gwlsBcbCzjQKXp3oV71dyHD.png)

在这个示例中,创建了三个名为print的重载函数(override functions),这些函数分别支持int、double和std::string类型的参数(arguments)。

6.4.3 参数类型转换

在函数重载过程中,编译器能够执行参数类型的转换操作以确定最适合的函数版本;默认参数以及数据类型的提升(例如从int到double)会对可重加载的函数进行选择产生影响

示例代码
复制代码
 #include <iostream>

    
  
    
 void display(int a) {
    
     std::cout << "Integer: " << a << std::endl;
    
 }
    
  
    
 void display(double b) {
    
     std::cout << "Double: " << b << std::endl;
    
 }
    
  
    
 int main() {
    
     display(10);    // 调用 display(int)
    
     display(3.14);  // 调用 display(double)
    
     display('A');   // 类型转换,调用 display(int)
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/KZ3rGcdRBYv0Ak5hDgLw76aFQ8lb.png)

在这个示例中,字符'A'被转换为int类型,因此调用了display(int)函数。

6.4.4 默认参数与重载

当实现重载函数时,可以选择为特定的参数设定默认值.若某个重载函数与带有默认参数的版本存在歧义,则会导致无法明确决定调用哪个版本.

示例代码
复制代码
 #include <iostream>

    
  
    
 void show(int a, int b = 10) {
    
     std::cout << "a: " << a << ", b: " << b << std::endl;
    
 }
    
  
    
 void show(double a) {
    
     std::cout << "a: " << a << std::endl;
    
 }
    
  
    
 int main() {
    
     show(5);      // 调用 show(int, int),使用默认参数
    
     show(3.14);   // 调用 show(double)
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/oB16e9AGKaRLmygnzFrEOWc3DHsN.png)

在这一示例中,在这种情况下

6.4.5 函数重载的限制

函数重载存在一定的限制条件,在于不允许仅仅根据返回类型的差异来进行功能扩展。当定义一个新函数时,则需确保其参数配置与其现有函数在数量或类型上产生明显差异

错误示例
复制代码
 #include <iostream>

    
  
    
 // 错误:仅通过返回类型不同来重载函数
    
 int getValue() {
    
     return 10;
    
 }
    
  
    
 double getValue() {
    
     return 3.14;
    
 }
    
  
    
 int main() {
    
     std::cout << getValue() << std::endl;  // 编译错误
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/0jsZqL4kifGm386rQCyI5YNMUwz7.png)

在该示例中,“两个getValue函数仅仅基于返回类型进行区分”,这违背了函数重载的规定,“导致编译失败”。

6.4.6 重载与命名空间

通过各自独立的命名空间支持定义同名的重载函数 命名空间提供了机制来组织代码并且有效地避免名称冲突

示例代码
复制代码
 #include <iostream>

    
  
    
 namespace FirstNamespace {
    
     void func() {
    
     std::cout << "Inside FirstNamespace" << std::endl;
    
     }
    
 }
    
  
    
 namespace SecondNamespace {
    
     void func() {
    
     std::cout << "Inside SecondNamespace" << std::endl;
    
     }
    
 }
    
  
    
 int main() {
    
     FirstNamespace::func();  // 调用 FirstNamespace 中的 func
    
     SecondNamespace::func(); // 调用 SecondNamespace 中的 func
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/LsXVS9ldJ5wBIOqEGYtFezN2oUQr.png)

在这个示例中,在不同的命名空间内具有相同名称的函数不会发生冲突;可以通过使用命名空间限定符来实现调用

重点与难点分析

重点

  1. 函数重载的基本概念和实现方法 :理解编译器如何通过创建同名函数但具有不同参数类型的机制来实现函数重载。
  2. 参数类型转换与重载选择 :了解编译器在进行参数类型转换的同时如何进行重载选择。
  3. 默认参数与重载 :熟悉在使用默认参数的情况下可能出现的二义性问题。

难点

  1. 参数类型转换带来的影响 :为了更好地理解参数类型转换对C++函数重载选择的影响规律,并且能够规避可能出现的异常情况。
  2. 熟悉掌握函数重载规则 以确保在实际编程过程中能够正确应用这些规则,并且能够有效防止违反相关规则导致编译错误。

总结与提高

本节总结

  1. 深入学习了函数重载的基本概念,并且熟练掌握了通过定义不同参数列表实现同名函数重载的具体方法。
  2. 深入理解了参数类型转换、默认参数设置与函数重载之间的关系,并成功避免在调用过程中出现的二义性问题。
  3. 借助于示例代码和练习题的学习过程,进一步加深了对函数重载这一知识点的理解与应用能力。

提高建议

  1. 深入练习函数重载的操作及其应用:通过编写各种包含重载函数的程序,熟悉这些操作的应用场景和方法。

  2. 深入理解参数类型转换的作用:通过实践掌握这些类型的转变如何影响程序的功能实现。

  3. 通过实践掌握参数类型转换在函数重载中的重要性:了解这种转变如何影响不同情境下的功能实现需求。

  4. 函数选择的因素之一是确保调用的是预期的重载函数。

  5. 3. 消除重载函数可能产生的歧义 :在编写重载函数时,科学配置参数列表,并防止由于默认参数设置或数据类型转换引起的歧义。

6.5 特殊用途语言特性

6.5.1 默认实参

默认实参让我们在函数声明中给形参设定一个默认值。当调用函数时没有传递相应的实参,则会使用默认值。采用默认实参与使得函数调用更加灵活,并简化了代码结构。

根据实际情况确定参数的取值范围

示例代码
复制代码
 #include <iostream>

    
  
    
 void print(int a = 10, int b = 20) {
    
     std::cout << "a: " << a << ", b: " << b << std::endl;
    
 }
    
  
    
 int main() {
    
     print();          // 使用默认实参
    
     print(30);        // b 使用默认实参
    
     print(40, 50);    // 不使用默认实参
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/KgbI9FswL0aQpkrd2nltX84Uvecf.png)

在这一实例中,在该程序运行过程中,在运行该函数时,在执行该操作的时候

6.5.2 constexpr函数

constexpr是一种在编译阶段计算并赋值给变量的关键字机制,在程序运行期间用于生成静态常量的一种方式。
它要求所有的返回值、参数以及内部使用的变量都必须限定为字面值类型,并且只能进行一次简单的赋值操作。
这种机制的一个显著优点是显著提升了代码在编译阶段的速度,并且降低了程序运行时对内存资源的需求。

示例代码
复制代码
 #include <iostream>

    
  
    
 constexpr int factorial(int n) {
    
     return (n <= 1) ? 1 : (n * factorial(n - 1));
    
 }
    
  
    
 int main() {
    
     constexpr int result = factorial(5);
    
     std::cout << "Factorial of 5 is " << result << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/xmp91HU7ZL20TGVb4wjQlNFOiRBe.png)

请看以下示例,在其中\texttt{factorial}函数被定义为一个\texttt{constexpr}类型,在编译阶段计算5!

6.5.3 内联函数

对于内联函数(inline function),通常会要求编译器将函数体直接插入到每个function call点,在这种情况下执行一次function call是不合适的。这样做能够降低频繁调用小型内联功能所导致的开销,并且这种方法特别适用于那些频繁被调用的小型内联功能场景。为了声明一个具有italicized style的行为,在代码中应使用inline关键字

示例代码
复制代码
 #include <iostream>

    
  
    
 inline int add(int a, int b) {
    
     return a + b;
    
 }
    
  
    
 int main() {
    
     int sum = add(3, 4);
    
     std::cout << "Sum: " << sum << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/uMGjwL8bgqHc9RsvXFY7K0tVDyNQ.png)

在这个示例中,add函数是一个内联函数,建议编译器将其内联展开。

6.5.4 调试支持

该程序支持以下两个调试特性:包含断言功能以及提供详细的.debuginfo文件内容显示。断言功能的作用是在程序运行过程中检测潜在的逻辑错误并触发警报或停止执行以供进一步分析。通过条件编译指令以及预处理宏来实现详细的.debuginfo文件内容显示。

示例代码
复制代码
 #include <iostream>

    
 #include <cassert>
    
  
    
 void checkPositive(int x) {
    
     assert(x > 0 && "Number is not positive");
    
     std::cout << x << " is positive." << std::endl;
    
 }
    
  
    
 int main() {
    
     checkPositive(10);
    
     // checkPositive(-5);  // 会触发断言失败
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/Tp7sNYzOL59tMxEwdrVUDjlgW2K6.png)

在这一实例中,在条件不满足的情况下(即当上述情况发生时),该宏用于验证变量x是否为正值,并将导致程序停止运行并输出错误提示。

NDEBUG预处理变量

在定义了\texttt{NDEBUG}预处理变量之后,在编译时该变量会被启用以排除某些调试信息。通常情况下,在最终的编译产物中会关闭\texttt{NOGDBUG}选项来去除开发调试信息。

示例代码
复制代码
 #include <iostream>

    
 #include <cassert>
    
  
    
 // #define NDEBUG  // 取消注释以禁用 assert
    
  
    
 void checkPositive(int x) {
    
     assert(x > 0 && "Number is not positive");
    
     std::cout << x << " is positive." << std::endl;
    
 }
    
  
    
 int main() {
    
     checkPositive(10);
    
     // checkPositive(-5);  // 如果定义了 NDEBUG,不会触发断言失败
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/AleSfEqtaIQJV7LyZgz0DYURv8ho.png)

在这个示例中,如果定义了NDEBUG,则assert宏将被禁用,不会进行检查。

6.5.5 使用递归

递归函数被称为在自身内部调用自身的函数。它通常用于解决能够被分解为更小的同类子问题的问题。为了防止无限循环必须定义一个基准情况以避免无限递归。

示例代码
复制代码
 #include <iostream>

    
  
    
 int fibonacci(int n) {
    
     if (n <= 1) return n;
    
     return fibonacci(n - 1) + fibonacci(n - 2);
    
 }
    
  
    
 int main() {
    
     int result = fibonacci(10);
    
     std::cout << "Fibonacci of 10 is " << result << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/UiB3wCJMrSZx2TKqabg5sFWhQIpd.png)

在这个示例中,fibonacci函数使用递归计算第10个斐波那契数。

尾递归属于递归的一种特殊类型,在程序流程中其核心特征在于函数体中的最后一步操作即为递归调用。这种优化使得编译器能够将尾递归转换为非递归的过程,并通过减少对栈结构的操作来降低执行过程中的资源消耗。值得注意的是C++并非强制要求编译器实现尾递归优化功能但在实践中许多编译器确实实现了这一功能

示例代码
复制代码
 #include <iostream>

    
  
    
 int factorial(int n, int accumulator = 1) {
    
     if (n <= 1) return accumulator;
    
     return factorial(n - 1, n * accumulator);
    
 }
    
  
    
 int main() {
    
     int result = factorial(5);
    
     std::cout << "Factorial of 5 is " << result << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/fF8xNhozQ3c69KdsSyT7mIEgVtAe.png)

在这一示例中,在这个例子中(或在此处),factorial函数采用尾递归方法来计算其阶乘值,并且递归调用被明确指定为函数体内的最后一步。

重点与难点分析

重点

  1. default arguments: Grasp the definition and practical applications of default arguments, and perfect the technique for implementing them.
  2. constexpr types: Grasp the definition and usage of constexpr types, comprehending their role in compile-time computations.
  3. inline functions: Understand the purpose and implementation of inline functions, and recognize their role in minimizing function call overhead.
  4. debugging features: Acquire the skills to utilize debugging features, including understanding how assert macros contribute to error reporting through conditional compilation and preprocessing.
  5. recursion and tail recursion optimization techniques: Understand the concepts of recursion and tail recursion optimization techniques, along with their typical applications.

难点

  1. 默认实参的二义性问题 :新手应掌握在 overloaded functions 中使用 default parameters 可能导致 的二义性问题。
  2. constexpr函数的限制 :新手应学习constexpr 函数需遵守 的约束条件 以满足 compile-time computations requirements。
  3. 尾递归优化的实现 :新手需学习 implementing tail recursion optimization 的方法 以 reduce call stack overhead 的作用。

练习题解析

  1. 练习6.25 :编写一个函数,使用默认实参计算两个数的和。
    • 示例代码
复制代码
 #include <iostream>

    
  
    
 int add(int a, int b = 5) {
    
     return a + b;
    
 }
    
  
    
 int main() {
    
     std::cout << add(3) << std::endl;    // 使用默认实参 b = 5
    
     std::cout << add(3, 7) << std::endl; // 不使用默认实参
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/6GPRd7uQjOr30fEwJyoWNkvLmMgZ.png)
  1. 练习6.26 :编写一个constexpr函数,计算一个数的平方。
    • 示例代码
复制代码
 #include <iostream>

    
  
    
 constexpr int square(int x) {
    
     return x * x;
    
 }
    
  
    
 int main() {
    
     constexpr int result = square(5);
    
     std::cout << "Square of 5 is " << result << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/KoAZEzXwkPu0hNlc38qmgt7sSyVC.png)
  1. 练习6.27 :编写一个内联函数,计算两个数的最小值。
    • 示例代码
复制代码
 #include <iostream>

    
  
    
 inline int min(int a, int b) {
    
     return (a < b) ? a : b;
    
 }
    
  
    
 int main() {
    
     int result = min(3, 7);
    
     std::cout << "Min: " << result << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/0IbQgkK16p2xcJ4PMSNeHVCoADRv.png)

练习6.28 :实现一个应用assert宏的函数用于检测数值是否为负,并通过NDEBUG预处理变量禁止启用assert宏。

    • 示例代码
复制代码
 #include <iostream>

    
 #include <cassert>
    
  
    
 // #define NDEBUG  // 取消注释以禁用 assert
    
  
    
 void checkNegative(int x) {
    
     assert(x < 0 && "Number is not negative");
    
     std::cout << x << " is negative." << std::endl;
    
 }
    
  
    
 int main() {
    
     checkNegative(-10);
    
     // checkNegative(5);  // 如果定义了 NDEBUG,不会触发断言失败
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/zxV4nypUh6f2RJksweKqcFZ1Sgt5.png)
  1. 练习6.29 :编写一个递归函数,计算一个数的阶乘,并实现尾递归优化。
    • 示例代码
复制代码
 #include <iostream>

    
  
    
 int factorial(int n) {
    
     if (n <= 1) return 1;
    
     return n * factorial(n - 1);
    
 }
    
  
    
 int factorialTail(int n, int accumulator = 1) {
    
     if (n <= 1) return accumulator;
    
     return factorialTail(n - 1, n * accumulator);
    
 }
    
  
    
 int main() {
    
     int result = factorial(5);
    
     std::cout << "Factorial of 5 is " << result << std::endl;
    
  
    
     int tailResult = factorialTail(5);
    
     std::cout << "Tail Factorial of 5 is " << tailResult << std::endl;
    
  
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/jq0vK5tEzMXwBrOaRDP9k8hL3fs6.png)

总结与提高

本节总结

  1. 深入学习并掌握了默认参数值的设置方法以及const表达式函数(即constexpr函数)的应用场景,并熟悉内联功能的设计原理及其在实际开发中的具体实现。
  2. 理解并深入掌握了各种特殊用途语言特性的作用机制及其编程实践,在提高编译效率与减少运行开销方面发挥了显著作用。
  3. 通过编写示例代码以及完成配套练习题集来深入理解并熟练运用这些高级特性。

提高建议

  1. 加强训练默认实参与内联函数的应用能力 :通过编写各种包含默认实参和内联函数的程序来强化对这些概念的理解及其实际应用。
  2. 透彻掌握constexpr属性及其调试支持特性 :通过实践操作深入了解并熟练运用constexpr属性及其相关的调试支持特性,在提升编译效率的同时确保代码运行的可靠性。
  3. 深入理解递归与尾递归优化的本质 :在编写递归函数时注重科学设计基准情况与递归步骤,并学习具体的尾递归优化实现方法,在保证程序正确性的同时显著提升代码运行效率。

6.6 函数匹配

6.6.1 函数匹配概述

在函数调用过程中(Function Matching),程序会根据提供的实参类型与数量来选择最适合的重载函数。当编译器遇到多个具有相同名称的函数时(if multiple functions with the same name exist in the codebase),它将依据函数匹配规则来确定哪个函数最合适地被调用(called)。如果没有找到合适的匹配方案(suitable match)或者出现歧义情况(ambiguity exists)的话(which may lead to ambiguity in function selection),编译器将会报错(result in a compilation error)

6.6.2 重载决议

overload resolution is a method that a compiler selects the most suitable overload function for a given set of parameters. When a compiler examines the function's parameter list and the actual arguments provided during invocation, it applies the following rules to perform overload resolution.

  1. 严格对应:输入参数类型与输出参数类型完全一致。
  2. 常规数据类型的转换包括例如将整数转为浮点数(如int转double),或者将字符转为整数(如char转int)。
  3. 自定义类型的变换通常借助构造函数或转型操作符完成。
  4. 未显式声明的默认参数会在输入参数数量不足时被编译器自动填充以满足需求。
示例代码
复制代码
 #include <iostream>

    
  
    
 void print(int a) {
    
     std::cout << "Integer: " << a << std::endl;
    
 }
    
  
    
 void print(double b) {
    
     std::cout << "Double: " << b << std::endl;
    
 }
    
  
    
 void print(const std::string &c) {
    
     std::cout << "String: " << c << std::endl;
    
 }
    
  
    
 int main() {
    
     print(10);           // 精确匹配 print(int)
    
     print(3.14);         // 精确匹配 print(double)
    
     print("Hello");      // 精确匹配 print(const std::string &)
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/HOufBJNWl52bcDSvXFsR3VKIent4.png)

在这个示例中,编译器根据实参的类型选择最合适的重载函数。

6.6.3 最佳匹配

当多个候选函数可供选择时,编译器倾向于采用“最优配对”的形式。最优配对被定义为实现实参与形参类型间转换成本最低的方式。若不存在单一最优配对情况,则程序将触发错误提示信息。

示例代码
复制代码
 #include <iostream>

    
  
    
 void func(int a) {
    
     std::cout << "Integer: " << a << std::endl;
    
 }
    
  
    
 void func(double b) {
    
     std::cout << "Double: " << b << std::endl;
    
 }
    
  
    
 void func(float c) {
    
     std::cout << "Float: " << c << std::endl;
    
 }
    
  
    
 int main() {
    
     func(10);      // 调用 func(int)
    
     func(3.14);    // 调用 func(double)
    
     func(2.5f);    // 调用 func(float)
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/QL7irvZ46BekDcmlOXxG3s1AaMNU.png)

在这个示例中,编译器选择与实参类型最匹配的函数进行调用。

6.6.4 二义性

当多个相同功能的重载函数被定义时,在不明确指定调用对象的情况下(即多个候选目标满足同样的条件),编译器将无法明确选择调用哪一个函数,并导致编译时出现二义性错误。可以通过确保重载函数的参数类型尽可能明确来消除这种不确定性。

示例代码
复制代码
 #include <iostream>

    
  
    
 void ambiguous(int a) {
    
     std::cout << "Integer: " << a << std::endl;
    
 }
    
  
    
 void ambiguous(double b) {
    
     std::cout << "Double: " << b << std::endl;
    
 }
    
  
    
 void ambiguous(long c) {
    
     std::cout << "Long: " << c << std::endl;
    
 }
    
  
    
 int main() {
    
     // ambiguous(10L);  // 错误:二义性,long 可匹配 int 和 double
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/QSb9WYB4zE8wLXcxyPJ5GNOTgdoM.png)

在这一例子中,在使用十进制数的情况下(即数值后添加了类型前缀),数值被识别为兼容于int或double类型之间的一种情况。尽管数值本身是十进制的(即基数为10),但因为没有明确指定对应的overload函数来处理这种转换关系(例如sin(10)可能会被解释为sin(10°)或者sin(10弧度)),因此会出现歧义性的编译错误。这种情况下必须明确指定所需的overload函数以避免歧义

6.6.5 默认实参与重载

当函数使用默认实参进行调用时, 必须谨慎地避免与函数重载之间的歧义性问题. 当默认参数与函数重载协同应用时, 必须保证只有一个对应的版本存在

示例代码
复制代码
 #include <iostream>

    
  
    
 void display(int a, int b = 10) {
    
     std::cout << "a: " << a << ", b: " << b << std::endl;
    
 }
    
  
    
 void display(double a) {
    
     std::cout << "a: " << a << std::endl;
    
 }
    
  
    
 int main() {
    
     display(5);     // 调用 display(int, int),使用默认参数
    
     display(3.14);  // 调用 display(double)
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/Hb7i1wphSkUTzn6sf5PDxuGtQlCX.png)

在这个示例中,在被调用时 display(5),它被 display(int, int) 并采用了默认参数;而 display(3.14) 则被 display(double) 所表示。

6.6.6 特殊匹配情况

可能存在的情况可能会影响函数匹配,例如使用模板函数和函数指针等.其中, 模板函数的匹配规则更为复杂,因为编译器依据模板实参推断和匹配规则来确定最终的匹配结果.

示例代码
复制代码
 #include <iostream>

    
  
    
 template <typename T>
    
 void templateFunc(T a) {
    
     std::cout << "Template: " << a << std::endl;
    
 }
    
  
    
 void templateFunc(int a) {
    
     std::cout << "Non-template: " << a << std::endl;
    
 }
    
  
    
 int main() {
    
     templateFunc(10);    // 调用非模板函数
    
     templateFunc(3.14);  // 调用模板函数
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/n6Y8oxEN917jeOtuvLPK3iTVUrfs.png)

在该案例中,templateFunc(10)被成功地调用而非模版函数,同时templateFunc(3.14)则被模版函数所调用。

重点与难点分析

重点

  1. 函数匹配规则:掌握精确地实现函数匹配机制的基本原则与步骤,并熟悉标准数据类型的转换过程以及根据具体需求进行自定义的数据映射。
  2. 最佳匹配:熟悉编译器在编译过程中进行的最佳化处理方式,并了解如何通过优先级规则确保解析的一致性。
  3. 默认实参与重载:对默认实参与重载结合使用时可能产生的问题及其解决方法有清晰的认识,并能够根据实际需求采取相应的优化策略。

难点

  1. 二义性问题:初学者应通过实践来理解其来源及应对策略。
  2. 模板函数匹配:需掌握其匹配规则,并了解涉及实参推断与配对过程中的难度。

总结与提高

本节总结

深入学习了函数匹配的核心规则,并熟悉了重载决议、最佳匹配以及如何解决二义性问题的方法。
透彻理解了默认实参与重载结合使用时的使用注意事项,并成功规避了二义性问题的发生。
通过编写示例代码并进行针对性练习,进一步加深了对函数匹配及其相关知识的理解。

模板函数匹配的理解和应用。

提高建议

  1. 加强练习函数匹配的定义与调用:通过编写各种包含重载函数的程序强化对函数匹配规则和重载决议的理解。
  2. 深入理解并掌握二义性问题:通过实践掌握二义性问题产生的原因及其解决方法以避免重载函数之间的冲突。
  3. 熟悉模板函数的匹配规则:在编写模板函数时合理设计模板参数以理解模板实参推断和匹配的复杂性从而提高代码灵活性和可维护性。

6.7 函数指针

6.7.1 函数指针概述

函数 pointers are pointers that point to functions. By using function pointers, we can indirectly invoke functions and pass them as arguments to other functions. This makes it easier to write flexible and reusable code.

6.7.2 定义和初始化函数指针

函数指针的定义包括返回类型和参数列表,形式如下:

复制代码
    returnType (*pointerName)(parameterList);
    
    cpp

要初始化函数指针,可以将其指向一个具有相同签名的函数。

示例代码
复制代码
 #include <iostream>

    
  
    
 // 函数定义
    
 int add(int a, int b) {
    
     return a + b;
    
 }
    
  
    
 int main() {
    
     // 定义函数指针
    
     int (*funcPtr)(int, int) = add;
    
     // 通过函数指针调用函数
    
     int result = funcPtr(3, 4);
    
     std::cout << "Result: " << result << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/p9lM4itqQ53brRgcHVOPIAwhNzYJ.png)

在此示例中,funcPtr充当一个指向add函数的函数指针,并通过此指针调用add函数从而实现目标功能。

6.7.3 函数指针作为参数

函数指针能够充当参数传送给其他程序或模块,并以灵活的方式调用不同的函数

示例代码
复制代码
 #include <iostream>

    
  
    
 // 函数定义
    
 int add(int a, int b) {
    
     return a + b;
    
 }
    
  
    
 int subtract(int a, int b) {
    
     return a - b;
    
 }
    
  
    
 // 接受函数指针作为参数的函数
    
 int compute(int (*func)(int, int), int x, int y) {
    
     return func(x, y);
    
 }
    
  
    
 int main() {
    
     // 调用 compute 函数,传递 add 和 subtract 函数指针
    
     int sum = compute(add, 5, 3);
    
     int difference = compute(subtract, 5, 3);
    
     std::cout << "Sum: " << sum << std::endl;
    
     std::cout << "Difference: " << difference << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/LUpPB4jTDalXO1viRCqt5b80NzSn.png)

在这个示例中,compute函数采用指定的函数指针来完成操作,并被用于执行相应的功能。

6.7.4 函数指针作为返回值

函数指针也可以作为函数的返回值,从而实现对不同函数的动态选择。

示例代码
复制代码
 #include <iostream>

    
  
    
 // 函数定义
    
 int add(int a, int b) {
    
     return a + b;
    
 }
    
  
    
 int subtract(int a, int b) {
    
     return a - b;
    
 }
    
  
    
 // 返回函数指针的函数
    
 int (*getOperation(char op))(int, int) {
    
     if (op == '+') {
    
     return add;
    
     } else {
    
     return subtract;
    
     }
    
 }
    
  
    
 int main() {
    
     // 获取函数指针并调用函数
    
     int (*operation)(int, int) = getOperation('+');
    
     int result = operation(10, 5);
    
     std::cout << "Result: " << result << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/FB8adXjrLGnuh3Av2Ko9kzVQStMq.png)

在以下示例中,在此实现中将输入的每个字符映射到相应的操作指针,并利用该操作指针来执行相关的操作。

6.7.5 使用typedef简化函数指针

借助typedef,我们可以提高函数指针定义与使用的效率,并使代码更加简洁易懂,并且更容易维护。

示例代码
复制代码
 #include <iostream>

    
  
    
 // 使用 typedef 定义函数指针类型
    
 typedef int (*operationFunc)(int, int);
    
  
    
 // 函数定义
    
 int add(int a, int b) {
    
     return a + b;
    
 }
    
  
    
 int subtract(int a, int b) {
    
     return a - b;
    
 }
    
  
    
 // 接受函数指针作为参数的函数
    
 int compute(operationFunc func, int x, int y) {
    
     return func(x, y);
    
 }
    
  
    
 int main() {
    
     // 使用 typedef 定义的函数指针类型
    
     operationFunc op = add;
    
     int result = compute(op, 7, 3);
    
     std::cout << "Result: " << result << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/t2KrlDNTiL1493QgcRxvIHwnYpyS.png)

6.7.6 使用using简化函数指针

C++11引入了一种新的关键字 using ,用于定义类型别名。
相比于 typedef

这种语法更加直观易懂。
尤其在涉及复杂的类型时非常有用

示例代码
复制代码
 #include <iostream>

    
  
    
 // 使用 using 定义函数指针类型
    
 using operationFunc = int(*)(int, int);
    
  
    
 // 函数定义
    
 int add(int a, int b) {
    
     return a + b;
    
 }
    
  
    
 int subtract(int a, int b) {
    
     return a - b;
    
 }
    
  
    
 // 接受函数指针作为参数的函数
    
 int compute(operationFunc func, int x, int y) {
    
     return func(x, y);
    
 }
    
  
    
 int main() {
    
     // 使用 using 定义的函数指针类型
    
     operationFunc op = add;
    
     int result = compute(op, 7, 3);
    
     std::cout << "Result: " << result << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/3KpTinEbXjZScJ9YFk7WAmO56GxM.png)

在上述示例中,在通过引入using关键字定义了类型operationFunc后,在函数指针的操作上实现了更加简洁的方式

6.7.7 指向成员函数的指针

动态链接库中的静态成员函数指针与全局静态成员函数指针之间存在本质区别。当声明一个指向静态成员功能的动态链接库指针变量时,必须明确指定该类型属于哪个类的对象实例作用域

示例代码
复制代码
 #include <iostream>

    
  
    
 class Calculator {
    
 public:
    
     int add(int a, int b) const {
    
     return a + b;
    
     }
    
  
    
     int subtract(int a, int b) const {
    
     return a - b;
    
     }
    
 };
    
  
    
 int main() {
    
     Calculator calc;
    
     // 定义指向成员函数的指针
    
     int (Calculator::*funcPtr)(int, int) const = &Calculator::add;
    
     // 通过指针调用成员函数
    
     int result = (calc.*funcPtr)(10, 5);
    
     std::cout << "Result: " << result << std::endl;
    
     return 0;
    
 }
    
    
    
    
    cpp
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/Qy84eCk3wnD1LaobUtzNuVh0dgTY.png)

在这个示例中,在计算器类的成员函数对象上存在一个名为funcPtr的指针变量。通过此指针能够调用计算器类中的add方法。

重点与难点分析

重点

  1. 深入学习并熟练掌握函数指针的定义方法及初始化操作,并能熟练调用其相关功能。
  2. 透彻理解如何将自定义的功能模块以函数指针的形式传递作为程序参数或返回至调用者处,并提升代码运行时的灵活性。
  3. 合理运用预定义数据类型关键字typedef与名称绑定机制using来简化对复杂功能模块对象的引用方式,并显著提升代码编写效率与可读性。
  4. 深入理解并正确运用指向成员类成员的具体方法(如对象实例)的功能模块对象的操作符重载实现方式及其特点,并明确区分其与普通全局或静态成员功能模块对象操作符重载之间的差异。

难点

  1. 函数指针的概念与应用 :为了深入理解程序设计中的功能特性与实现细节, 初学者应通过大量实践来熟练掌握各种编程语言中的典型实例.
  2. 关于成员操作符的理解与应用 :为了更好地理解面向对象程序设计的基本思想, 初学者应深入探究对象间关系的本质特征.

总结与提高

本节总结

  1. 深入理解了函数指针的概念及其初始化方法,并熟练掌握了通过引用实现对其他程序文件中相应功能的方式。
  2. 函数指针不仅能够作为程序运行时传递给其他程序文件中的相应功能进行操作,在具体实现中还具有灵活多样的应用方式。
  3. 在实际编程过程中发现利用typedef关键字配合using关键字能显著优化代码结构并提高可读性。
  4. 指向成员函数的指针不仅能够直接访问对象内部封装的数据成员还可以方便地传递给外部需要处理的对象属性信息。

提高建议

  1. 加强掌握函数指针的概念及应用:通过设计并实现不同场景下的程序实例,在实践中加深对函数指针语法特性和实际运用的理解。
  2. 深入探究指向成员函数的技术细节:通过动手操作全面掌握指向成员函数的操作规范及其与普通函数指针的区别所在。
  3. 合理运用C++中的预处理指令以提升代码清晰度:在开发复杂的涉及大量函数指针的应用程序时,在确保功能正确的同时采用合适的预处理指令来简化定义流程并提升代码整体可读性和维护性。

本主页会定期发布相关内容,请随时留意最新的更新信息。除此之外,请通过微信搜索并订阅该公众号:AI与编程之窗。或扫描下方二维码直接订阅该公众号。这样您可以方便地接收最新的内容推送。

全部评论 (0)

还没有任何评论哟~