用户工具

站点工具


cplus:第四章函数

第四章 函数

本章将详细介绍C++中的函数,包括函数的定义与调用、函数重载、默认参数、内联函数、递归等核心概念。

4.1 函数基础

4.1.1 函数的定义

函数是完成特定任务的独立代码块,由函数头和函数体组成。

// 函数的基本结构
返回类型 函数名(参数列表) {
    // 函数体
    return 返回值;  // 如果返回类型不是void
}
 
// 具体示例
#include <iostream>
using namespace std;
 
// 无参数、无返回值的函数
void sayHello() {
    cout << "Hello, World!" << endl;
}
 
// 有参数、无返回值的函数
void greet(string name) {
    cout << "Hello, " << name << "!" << endl;
}
 
// 有参数、有返回值的函数
int add(int a, int b) {
    return a + b;
}
 
// 多个参数的函数
double calculateArea(double length, double width) {
    return length * width;
}
 
int main() {
    sayHello();
    greet("Alice");
 
    int sum = add(10, 20);
    cout << "Sum: " << sum << endl;
 
    double area = calculateArea(5.5, 3.0);
    cout << "Area: " << area << endl;
 
    return 0;
}

4.1.2 函数声明与定义分离

// math_utils.h - 头文件
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
 
// 函数声明(原型)
int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
double divide(int a, int b);
 
#endif
// math_utils.cpp - 实现文件
#include "math_utils.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(int a, int b) {
    if (b == 0) {
        // 错误处理
        return 0;
    }
    return static_cast<double>(a) / b;
}
// main.cpp
#include <iostream>
#include "math_utils.h"
 
using namespace std;
 
int main() {
    cout << "10 + 5 = " << add(10, 5) << endl;
    cout << "10 - 5 = " << subtract(10, 5) << endl;
    cout << "10 * 5 = " << multiply(10, 5) << endl;
    cout << "10 / 5 = " << divide(10, 5) << endl;
    return 0;
}

4.1.3 参数传递方式

#include <iostream>
using namespace std;
 
// 1. 值传递(默认)- 创建副本
void swapByValue(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
    cout << "Inside swapByValue: a=" << a << ", b=" << b << endl;
}
 
// 2. 引用传递 - 操作原变量
void swapByReference(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}
 
// 3. 指针传递 - 通过地址操作
void swapByPointer(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}
 
// 4. const引用传递 - 避免拷贝,保护原数据
void printLargeObject(const vector<int>& vec) {
    // vec.push_back(1);  // 错误!不能修改const引用
    for (int x : vec) {
        cout << x << " ";
    }
    cout << endl;
}
 
int main() {
    int x = 10, y = 20;
 
    // 值传递 - 不改变原变量
    swapByValue(x, y);
    cout << "After swapByValue: x=" << x << ", y=" << y << endl;
 
    // 引用传递 - 改变原变量
    swapByReference(x, y);
    cout << "After swapByReference: x=" << x << ", y=" << y << endl;
 
    // 指针传递 - 改变原变量
    swapByPointer(&x, &y);
    cout << "After swapByPointer: x=" << x << ", y=" << y << endl;
 
    // const引用
    vector<int> large(1000, 1);
    printLargeObject(large);
 
    return 0;
}

4.2 函数重载

4.2.1 重载基础

函数重载允许在同一作用域内定义多个同名函数,只要它们的参数列表不同。

#include <iostream>
#include <string>
using namespace std;
 
// 重载函数:同名不同参数
int add(int a, int b) {
    cout << "int add" << endl;
    return a + b;
}
 
double add(double a, double b) {
    cout << "double add" << endl;
    return a + b;
}
 
int add(int a, int b, int c) {
    cout << "three int add" << endl;
    return a + b + c;
}
 
string add(const string& a, const string& b) {
    cout << "string add" << endl;
    return a + b;
}
 
int main() {
    cout << add(1, 2) << endl;           // 调用 int add
    cout << add(1.5, 2.5) << endl;       // 调用 double add
    cout << add(1, 2, 3) << endl;        // 调用 three int add
    cout << add(string("Hello"), string(" World")) << endl;
 
    return 0;
}

4.2.2 重载解析规则

#include <iostream>
using namespace std;
 
void func(int a) {
    cout << "func(int)" << endl;
}
 
void func(double a) {
    cout << "func(double)" << endl;
}
 
void func(int a, int b) {
    cout << "func(int, int)" << endl;
}
 
void func(long a) {
    cout << "func(long)" << endl;
}
 
int main() {
    func(10);      // 精确匹配 func(int)
    func(3.14);    // 精确匹配 func(double)
    func(10, 20);  // 精确匹配 func(int, int)
 
    // func(3.14f);  // 可以匹配 func(int) 或 func(double)
                      // 会选择 func(double),因为float到double是提升
 
    // 歧义调用
    // func(10L);    // 错误:可以匹配 func(int) 或 func(long)
 
    // 显式转换解决歧义
    func(static_cast<int>(10L));
    func(static_cast<long>(10L));
 
    return 0;
}

4.2.3 重载的限制

#include <iostream>
using namespace std;
 
// 返回类型不能用于区分重载
// int getValue() { return 0; }
// double getValue() { return 0.0; }  // 错误:重定义
 
// 默认参数可能导致歧义
void print(int a, int b = 10) {
    cout << "print(int, int)" << endl;
}
 
void print(int a) {
    cout << "print(int)" << endl;
}
 
int main() {
    print(5, 10);  // 调用 print(int, int)
    // print(5);   // 错误:歧义,可以匹配两个函数
 
    return 0;
}

4.3 默认参数

4.3.1 默认参数的使用

#include <iostream>
#include <string>
using namespace std;
 
// 默认参数必须从右向左连续
void printInfo(string name, int age = 18, string country = "China") {
    cout << "Name: " << name << endl;
    cout << "Age: " << age << endl;
    cout << "Country: " << country << endl;
    cout << endl;
}
 
// 函数声明中指定默认参数,定义中不再指定
void connect(string host, int port = 8080);
 
void connect(string host, int port) {
    cout << "Connecting to " << host << ":" << port << endl;
}
 
// 默认参数与重载结合
class Box {
public:
    // 构造函数重载
    Box() : width(1), height(1), depth(1) {}
    Box(double w) : width(w), height(1), depth(1) {}
    Box(double w, double h) : width(w), height(h), depth(1) {}
    Box(double w, double h, double d) : width(w), height(h), depth(d) {}
 
    // 或者用默认参数(更简洁)
    // Box(double w = 1, double h = 1, double d = 1) 
    //     : width(w), height(h), depth(d) {}
 
private:
    double width, height, depth;
};
 
int main() {
    printInfo("Alice");                    // 使用所有默认值
    printInfo("Bob", 25);                  // 使用country的默认值
    printInfo("Charlie", 30, "USA");       // 不使用默认值
 
    connect("localhost");                  // 使用默认端口8080
    connect("localhost", 3000);            // 指定端口
 
    return 0;
}

4.3.2 默认参数的注意事项

#include <iostream>
using namespace std;
 
// 1. 默认参数不能重复指定
void func(int a, int b = 10);
// void func(int a, int b = 10) { }  // 定义时不能再指定默认参数
void func(int a, int b) { }
 
// 2. 默认参数在声明中指定,头文件中可见
// 头文件:void draw(int x, int y, int color = 0xFFFFFF);
 
// 3. 默认参数可以是表达式
int getDefaultValue() {
    return 100;
}
 
void setValue(int val = getDefaultValue()) {
    cout << "Value: " << val << endl;
}
 
// 4. 默认参数与函数指针
void process(int a, int b = 10);
 
int main() {
    void (*ptr1)(int, int) = process;     // 正确
    // void (*ptr2)(int) = process;       // 错误,类型不匹配
 
    setValue();      // 使用默认值100
    setValue(50);    // 使用50
 
    return 0;
}

4.4 内联函数

4.4.1 内联函数的概念

内联函数建议编译器将函数调用替换为函数体,减少函数调用的开销。

#include <iostream>
using namespace std;
 
// 普通函数
int maxNormal(int a, int b) {
    return (a > b) ? a : b;
}
 
// 内联函数
inline int maxInline(int a, int b) {
    return (a > b) ? a : b;
}
 
// 内联函数与宏的比较
#define MAX_MACRO(a, b) ((a) > (b) ? (a) : (b))
 
inline int maxInlineSafe(int a, int b) {
    return (a > b) ? a : b;
}
 
int main() {
    int x = 5, y = 3;
 
    // 宏的问题
    int result1 = MAX_MACRO(x++, y++);
    // 展开为: ((x++) > (y++) ? (x++) : (y++))
    // x和y可能被增加两次!
 
    // 内联函数安全
    int result2 = maxInlineSafe(x++, y++);
    // x和y只增加一次
 
    cout << "Macro result: " << result1 << endl;
    cout << "Inline result: " << result2 << endl;
 
    // 现代C++:使用constexpr函数(C++11)
    constexpr int maxConstexpr(int a, int b) {
        return (a > b) ? a : b;
    }
 
    constexpr int m = maxConstexpr(10, 20);  // 编译时计算
 
    return 0;
}

4.4.2 内联的使用建议

// 适合内联的情况:
// 1. 函数体很小(1-3行)
// 2. 频繁调用的简单函数
// 3. 在头文件中定义
 
// inline.h
#ifndef INLINE_H
#define INLINE_H
 
inline int square(int x) {
    return x * x;
}
 
inline double circleArea(double radius) {
    const double PI = 3.14159;
    return PI * radius * radius;
}
 
// 类中的内联成员函数
class Point {
public:
    // 隐式内联
    int getX() const { return x; }
    int getY() const { return y; }
 
    // 显式内联
    inline void setX(int newX) { x = newX; }
    inline void setY(int newY) { y = newY; }
 
private:
    int x, y;
};
 
#endif

4.5 递归函数

4.5.1 递归基础

递归是函数调用自身的编程技术,需要基准条件和递归步骤。

#include <iostream>
using namespace std;
 
// 阶乘:n! = n * (n-1)!
unsigned long long factorial(int n) {
    // 基准条件
    if (n <= 1) {
        return 1;
    }
    // 递归步骤
    return n * factorial(n - 1);
}
 
// 斐波那契数列
unsigned long long fibonacci(int n) {
    if (n <= 1) {
        return n;
    }
    return fibonacci(n - 1) + fibonacci(n - 2);
}
 
// 改进的斐波那契(使用记忆化)
unsigned long long fibMemo(int n, unsigned long long* memo) {
    if (n <= 1) return n;
    if (memo[n] != 0) return memo[n];
 
    memo[n] = fibMemo(n - 1, memo) + fibMemo(n - 2, memo);
    return memo[n];
}
 
unsigned long long fibonacciFast(int n) {
    unsigned long long* memo = new unsigned long long[n + 1]();
    unsigned long long result = fibMemo(n, memo);
    delete[] memo;
    return result;
}
 
int main() {
    cout << "Factorial:" << endl;
    for (int i = 0; i <= 10; i++) {
        cout << i << "! = " << factorial(i) << endl;
    }
 
    cout << "\nFibonacci:" << endl;
    for (int i = 0; i <= 10; i++) {
        cout << "F(" << i << ") = " << fibonacciFast(i) << endl;
    }
 
    return 0;
}

4.5.2 更多递归示例

#include <iostream>
using namespace std;
 
// 幂运算
double power(double base, int exp) {
    if (exp == 0) return 1;
    if (exp < 0) return 1 / power(base, -exp);
 
    double half = power(base, exp / 2);
    if (exp % 2 == 0) {
        return half * half;
    } else {
        return half * half * base;
    }
}
 
// 欧几里得算法(最大公约数)
int gcd(int a, int b) {
    if (b == 0) return a;
    return gcd(b, a % b);
}
 
// 汉诺塔
void hanoi(int n, char from, char aux, char to) {
    if (n == 1) {
        cout << "Move disk 1 from " << from << " to " << to << endl;
        return;
    }
    hanoi(n - 1, from, to, aux);
    cout << "Move disk " << n << " from " << from << " to " << to << endl;
    hanoi(n - 1, aux, from, to);
}
 
// 二分查找(递归实现)
int binarySearch(int arr[], int left, int right, int target) {
    if (left > right) return -1;
 
    int mid = left + (right - left) / 2;
 
    if (arr[mid] == target) return mid;
    if (arr[mid] > target) {
        return binarySearch(arr, left, mid - 1, target);
    }
    return binarySearch(arr, mid + 1, right, target);
}
 
// 字符串反转
void reverseString(char str[], int left, int right) {
    if (left >= right) return;
 
    swap(str[left], str[right]);
    reverseString(str, left + 1, right - 1);
}
 
int main() {
    cout << "2^10 = " << power(2, 10) << endl;
    cout << "GCD(48, 18) = " << gcd(48, 18) << endl;
 
    cout << "\nHanoi (3 disks):" << endl;
    hanoi(3, 'A', 'B', 'C');
 
    int arr[] = {1, 3, 5, 7, 9, 11, 13};
    int index = binarySearch(arr, 0, 6, 7);
    cout << "\nBinary search: 7 is at index " << index << endl;
 
    char str[] = "Hello";
    reverseString(str, 0, 4);
    cout << "Reversed: " << str << endl;
 
    return 0;
}

4.6 函数指针与回调

4.6.1 函数指针

#include <iostream>
using namespace std;
 
// 普通函数
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; }
 
// 使用函数指针
int main() {
    // 定义函数指针
    int (*operation)(int, int);
 
    // 赋值
    operation = add;
    cout << "10 + 5 = " << operation(10, 5) << endl;
 
    operation = subtract;
    cout << "10 - 5 = " << operation(10, 5) << endl;
 
    operation = multiply;
    cout << "10 * 5 = " << operation(10, 5) << endl;
 
    // 函数指针数组
    int (*ops[])(int, int) = {add, subtract, multiply};
    const char* names[] = {"Add", "Subtract", "Multiply"};
 
    for (int i = 0; i < 3; i++) {
        cout << names[i] << ": " << ops[i](8, 3) << endl;
    }
 
    // 使用using定义类型别名(C++11)
    using Operation = int (*)(int, int);
    Operation op = add;
    cout << op(20, 10) << endl;
 
    // 使用auto(C++11)
    auto autoOp = multiply;
    cout << autoOp(5, 6) << endl;
 
    return 0;
}

4.6.2 回调函数

#include <iostream>
#include <vector>
using namespace std;
 
// 回调函数类型
using CompareFunc = bool (*)(int, int);
 
// 冒泡排序,接受比较函数
void bubbleSort(vector<int>& arr, CompareFunc compare) {
    int n = arr.size();
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (compare(arr[j], arr[j + 1])) {
                swap(arr[j], arr[j + 1]);
            }
        }
    }
}
 
// 比较函数
bool ascending(int a, int b) { return a > b; }
bool descending(int a, int b) { return a < b; }
 
// 回调函数的另一个例子:遍历数组
void forEach(const vector<int>& arr, void (*callback)(int)) {
    for (int x : arr) {
        callback(x);
    }
}
 
void printElement(int x) {
    cout << x << " ";
}
 
void printSquare(int x) {
    cout << x * x << " ";
}
 
int main() {
    vector<int> numbers = {64, 34, 25, 12, 22, 11, 90};
 
    cout << "Original: ";
    forEach(numbers, printElement);
    cout << endl;
 
    // 升序排序
    bubbleSort(numbers, ascending);
    cout << "Ascending: ";
    forEach(numbers, printElement);
    cout << endl;
 
    // 降序排序
    bubbleSort(numbers, descending);
    cout << "Descending: ";
    forEach(numbers, printElement);
    cout << endl;
 
    // 打印平方
    cout << "Squares: ";
    forEach(numbers, printSquare);
    cout << endl;
 
    return 0;
}

4.7 Lambda表达式(C++11)

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
 
int main() {
    vector<int> numbers = {1, 2, 3, 4, 5};
 
    // 基本lambda语法:[捕获](参数) -> 返回类型 { 函数体 }
 
    // 1. 最简单的lambda
    auto sayHello = []() { cout << "Hello!" << endl; };
    sayHello();
 
    // 2. 带参数的lambda
    auto add = [](int a, int b) -> int { return a + b; };
    cout << "3 + 5 = " << add(3, 5) << endl;
 
    // 3. 捕获外部变量
    int x = 10;
    int y = 20;
 
    // 值捕获
    auto captureByValue = [x, y]() { return x + y; };
    x = 100;  // 不影响lambda内部的x
    cout << "Capture by value: " << captureByValue() << endl;  // 30
 
    // 引用捕获
    x = 10;
    auto captureByRef = [&x, &y]() { return x + y; };
    x = 100;  // 影响lambda内部的x
    cout << "Capture by reference: " << captureByRef() << endl;  // 120
 
    // 隐式捕获
    auto implicitValue = [=]() { return x + y; };   // 全部值捕获
    auto implicitRef = [&]() { return x + y; };     // 全部引用捕获
    auto mixed = [=, &x]() { return x + y; };       // y值捕获,x引用捕获
 
    // 4. 在算法中使用lambda
    vector<int> nums = {5, 2, 8, 1, 9, 3};
 
    // 排序
    sort(nums.begin(), nums.end(), [](int a, int b) {
        return a > b;  // 降序
    });
 
    cout << "Sorted: ";
    for (int n : nums) cout << n << " ";
    cout << endl;
 
    // 查找第一个大于5的元素
    auto it = find_if(nums.begin(), nums.end(), [](int n) {
        return n > 5;
    });
    if (it != nums.end()) {
        cout << "First > 5: " << *it << endl;
    }
 
    // 5. 泛型lambda(C++14)
    auto genericAdd = [](auto a, auto b) { return a + b; };
    cout << "Add ints: " << genericAdd(1, 2) << endl;
    cout << "Add doubles: " << genericAdd(1.5, 2.5) << endl;
    cout << "Concat strings: " << genericAdd(string("Hello"), string(" World")) << endl;
 
    return 0;
}

4.8 本章小结

本章我们学习了:

  • 函数的定义、声明和调用
  • 三种参数传递方式:值传递、引用传递、指针传递
  • 函数重载的规则和限制
  • 默认参数的使用和注意事项
  • 内联函数的概念和使用建议
  • 递归函数的设计和优化
  • 函数指针和回调函数
  • Lambda表达式(C++11)

练习题

基础练习:

1. 编写重载函数abs,分别处理int、double和long

2. 实现一个函数swap,使用指针交换两个double的值

3. 编写递归函数计算字符串长度

4. 使用默认参数实现print函数,可以打印1到3个整数

进阶练习:

5. 实现快排算法,使用函数指针/回调实现自定义比较

6. 编写函数实现任意进制的数字转换(2-36进制)

7. 使用递归实现目录遍历(模拟,不需要实际文件操作)

思考题:

8. 比较内联函数和宏的优缺点,什么情况下应该选择内联函数?

9. 递归和迭代如何选择?各有什么优缺点?

10. 研究尾递归优化,C++编译器是否支持?

参考答案

练习1参考:

int abs(int n) { return n < 0 ? -n : n; }
double abs(double n) { return n < 0 ? -n : n; }
long abs(long n) { return n < 0 ? -n : n; }

练习3参考:

size_t strLength(const char* str) {
    if (*str == '\0') return 0;
    return 1 + strLength(str + 1);
}

练习5参考:

void quickSort(int arr[], int left, int right, bool (*compare)(int, int)) {
    if (left >= right) return;
 
    int pivot = arr[(left + right) / 2];
    int i = left, j = right;
 
    while (i <= j) {
        while (compare(arr[i], pivot)) i++;
        while (compare(pivot, arr[j])) j--;
        if (i <= j) {
            swap(arr[i], arr[j]);
            i++;
            j--;
        }
    }
 
    quickSort(arr, left, j, compare);
    quickSort(arr, i, right, compare);
}

下一章

继续学习:第五章 类与对象

cplus/第四章函数.txt · 最后更改: 127.0.0.1