C++ Primer 第6章:函数

C++ Primer 第6章:函数


6.1 函数基础

6.1.1 函数的定义

复制代码
函数定义的组成:
┌─────────────────────────────────────────────┐
│ 返回类型  函数名  (参数列表)                  │
│ {                                            │
│     函数体                                   │
│     return 语句;                             │
│ }                                            │
└─────────────────────────────────────────────┘
cpp 复制代码
// function_basics.cpp -- 函数基础
#include <iostream>
#include <string>
#include <vector>

// 函数定义
int add(int a, int b)
{
    return a + b;
}

// 无返回值函数
void printLine(int n, char ch = '-')
{
    for (int i = 0; i < n; i++)
        std::cout << ch;
    std::cout << std::endl;
}

// 返回 bool 的函数
bool isEven(int n)
{
    return n % 2 == 0;
}

// 返回 string 的函数
std::string greet(const std::string& name)
{
    return "Hello, " + name + "!";
}

int main()
{
    using namespace std;

    // 函数调用
    int sum = add(3, 4);
    cout << "3 + 4 = " << sum << endl;

    printLine(20);
    printLine(20, '*');

    cout << boolalpha;
    cout << "4是偶数:" << isEven(4) << endl;
    cout << "7是偶数:" << isEven(7) << endl;

    cout << greet("Alice") << endl;

    return 0;
}

6.1.2 函数的调用过程

cpp 复制代码
// call_process.cpp -- 函数调用过程
#include <iostream>

// 调用函数时发生的事情:
// 1. 用实参初始化形参
// 2. 将控制权转移给被调函数
// 3. 执行函数体
// 4. 执行 return 语句,返回值(可选)
// 5. 控制权返回调用点

int factorial(int n)
{
    // 函数体中可以定义局部变量
    int result = 1;
    for (int i = 2; i <= n; i++)
        result *= i;
    return result;   // 返回值
}

// 函数可以有多个 return 语句
int absVal(int n)
{
    if (n >= 0)
        return n;    // 第一个 return
    else
        return -n;   // 第二个 return
}

// void 函数可以有 return 语句(提前返回)
void printPositive(int n)
{
    if (n <= 0)
        return;   // 提前返回,不执行后续代码
    std::cout << n << " 是正数" << std::endl;
}

int main()
{
    using namespace std;

    cout << "5! = " << factorial(5) << endl;   // 120
    cout << "|-3| = " << absVal(-3) << endl;   // 3
    printPositive(5);
    printPositive(-3);   // 不输出任何内容

    return 0;
}

6.1.3 局部对象

cpp 复制代码
// local_objects.cpp -- 局部对象
#include <iostream>

// 局部变量:在函数内部定义,函数结束时销毁
// 局部静态变量:在函数内部定义,程序结束时销毁

int countCalls()
{
    static int count = 0;   // 局部静态变量:只初始化一次
    return ++count;
}

void demoLocal()
{
    int x = 10;   // 局部变量,每次调用都重新初始化
    static int y = 0;   // 局部静态变量,保留上次的值
    x++;
    y++;
    std::cout << "x=" << x << " y=" << y << std::endl;
}

int main()
{
    using namespace std;

    // 局部静态变量
    cout << "调用次数:" << countCalls() << endl;   // 1
    cout << "调用次数:" << countCalls() << endl;   // 2
    cout << "调用次数:" << countCalls() << endl;   // 3

    cout << "\n局部变量 vs 静态变量:" << endl;
    demoLocal();   // x=11 y=1
    demoLocal();   // x=11 y=2(x每次重置,y保留)
    demoLocal();   // x=11 y=3

    return 0;
}

6.1.4 函数声明(原型)

cpp 复制代码
// function_declaration.cpp -- 函数声明
#include <iostream>

// 函数声明(原型):告诉编译器函数的接口
// 可以在头文件中声明,在源文件中定义
double power(double base, int exp);   // 声明
bool   isPrime(int n);                // 声明

int main()
{
    using namespace std;

    // 可以在定义之前调用(因为有声明)
    cout << "2^10 = " << power(2, 10) << endl;
    cout << "17是质数:" << boolalpha << isPrime(17) << endl;

    return 0;
}

// 函数定义(可以在 main 之后)
double power(double base, int exp)
{
    double result = 1.0;
    for (int i = 0; i < exp; i++)
        result *= base;
    return result;
}

bool isPrime(int n)
{
    if (n < 2) return false;
    for (int i = 2; i * i <= n; i++)
        if (n % i == 0) return false;
    return true;
}

6.2 参数传递

6.2.1 传值参数

cpp 复制代码
// pass_by_value.cpp -- 传值参数
#include <iostream>

// 传值:函数接收实参的副本,修改不影响原变量
void increment(int n)
{
    n++;   // 修改的是副本
    std::cout << "函数内 n = " << n << std::endl;
}

// 传指针:通过指针可以修改原变量
void incrementPtr(int* p)
{
    (*p)++;   // 通过指针修改原变量
}

// 传指针:重置指针(只影响局部指针副本)
void resetPtr(int* p)
{
    p = nullptr;   // 只修改局部指针副本,不影响原指针
}

int main()
{
    using namespace std;

    int x = 5;

    // 传值:x 不变
    increment(x);
    cout << "调用后 x = " << x << endl;   // 5

    // 传指针:x 改变
    incrementPtr(&x);
    cout << "incrementPtr后 x = " << x << endl;   // 6

    // 传指针:指针本身不变
    int* p = &x;
    resetPtr(p);
    cout << "resetPtr后 p = " << (p ? "非空" : "空") << endl;   // 非空

    return 0;
}

6.2.2 传引用参数

cpp 复制代码
// pass_by_reference.cpp -- 传引用参数
#include <iostream>
#include <string>

// 引用参数:函数可以修改实参
void swap(int& a, int& b)
{
    int temp = a;
    a = b;
    b = temp;
}

// const 引用:只读,避免拷贝大对象
void printString(const std::string& s)
{
    std::cout << s << " (长度:" << s.size() << ")" << std::endl;
}

// 通过引用返回多个值
void minMax(const std::vector<int>& v, int& minVal, int& maxVal)
{
    minVal = maxVal = v[0];
    for (int x : v)
    {
        if (x < minVal) minVal = x;
        if (x > maxVal) maxVal = x;
    }
}

// 引用参数 vs 指针参数
void byRef(int& r)  { r = 100; }
void byPtr(int* p)  { *p = 100; }

int main()
{
    using namespace std;

    // 引用交换
    int a = 5, b = 10;
    cout << "交换前:a=" << a << " b=" << b << endl;
    swap(a, b);
    cout << "交换后:a=" << a << " b=" << b << endl;

    // const 引用
    string longStr = "这是一个很长的字符串,通过const引用传递避免拷贝";
    printString(longStr);

    // 通过引用返回多个值
    vector<int> nums = {3, 1, 4, 1, 5, 9, 2, 6};
    int minV, maxV;
    minMax(nums, minV, maxV);
    cout << "最小值:" << minV << " 最大值:" << maxV << endl;

    // 引用 vs 指针
    int x = 0;
    byRef(x);
    cout << "byRef后 x = " << x << endl;   // 100

    x = 0;
    byPtr(&x);
    cout << "byPtr后 x = " << x << endl;   // 100

    return 0;
}

6.2.3 const 形参和实参

cpp 复制代码
// const_params.cpp -- const形参
#include <iostream>
#include <string>

// 顶层 const 形参:调用时可以传 const 或非 const 实参
// 但函数内不能修改形参
void func1(const int i)   // 顶层const,调用者无感知
{
    // i = 10;   // ❌ 不能修改
    std::cout << "i = " << i << std::endl;
}

// 底层 const(指向const的指针):不能通过指针修改所指对象
void func2(const int* p)
{
    // *p = 10;   // ❌ 不能修改
    std::cout << "*p = " << *p << std::endl;
}

// const 引用:不能修改引用的对象
void func3(const int& r)
{
    // r = 10;   // ❌ 不能修改
    std::cout << "r = " << r << std::endl;
}

// ⚠️ 顶层const在函数重载中不区分
// void func(int i) {}
// void func(const int i) {}   // ❌ 重复定义!

// 但底层const可以重载
void print(int* p)        { std::cout << "非const指针" << std::endl; }
void print(const int* p)  { std::cout << "const指针" << std::endl; }

int main()
{
    using namespace std;

    int x = 5;
    const int cx = 10;

    func1(x);    // ✅ 传非const
    func1(cx);   // ✅ 传const

    func2(&x);   // ✅ 传非const指针
    func2(&cx);  // ✅ 传const指针

    func3(x);    // ✅ 传非const
    func3(cx);   // ✅ 传const
    func3(42);   // ✅ 传字面值(const引用可以绑定右值)

    print(&x);   // 调用非const版本
    print(&cx);  // 调用const版本

    return 0;
}

6.2.4 数组形参

cpp 复制代码
// array_params.cpp -- 数组形参
#include <iostream>
#include <string>

// 数组作为参数时,退化为指针
// 以下三种声明等价:
void print1(const int* arr, int size);
void print2(const int arr[], int size);
void print3(const int arr[10], int size);   // 10被忽略

void print1(const int* arr, int size)
{
    for (int i = 0; i < size; i++)
        std::cout << arr[i] << " ";
    std::cout << std::endl;
}

// 使用标准库 begin/end 传递范围
void printRange(const int* beg, const int* end)
{
    while (beg != end)
        std::cout << *beg++ << " ";
    std::cout << std::endl;
}

// 多维数组:必须指定除第一维外的所有维度
void print2D(int (*arr)[4], int rows)   // 指向含4个int的数组的指针
{
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < 4; j++)
            std::cout << arr[i][j] << "\t";
        std::cout << std::endl;
    }
}

// 数组引用形参(保留数组大小信息)
void printRef(const int (&arr)[5])   // 只能接受大小为5的数组
{
    for (int x : arr)
        std::cout << x << " ";
    std::cout << std::endl;
}

int main()
{
    using namespace std;

    int arr[] = {1, 2, 3, 4, 5};
    int size = sizeof(arr) / sizeof(arr[0]);

    print1(arr, size);
    printRange(begin(arr), end(arr));
    printRef(arr);   // 只能传大小为5的数组

    int matrix[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
    print2D(matrix, 3);

    return 0;
}

6.2.5 main 函数的参数

cpp 复制代码
// main_params.cpp -- main函数的参数
#include <iostream>
#include <string>

// main 函数可以接受命令行参数
// argc:参数个数(包括程序名)
// argv:参数字符串数组
int main(int argc, char* argv[])
{
    using namespace std;

    cout << "参数个数:" << argc << endl;
    cout << "程序名:" << argv[0] << endl;

    for (int i = 1; i < argc; i++)
        cout << "参数" << i << ":" << argv[i] << endl;

    // 也可以写成:
    // int main(int argc, char** argv)

    // 实际使用示例
    if (argc < 2)
    {
        cout << "用法:" << argv[0] << " <名字>" << endl;
        return 1;
    }

    string name = argv[1];
    cout << "Hello, " << name << "!" << endl;

    return 0;
}

6.2.6 可变参数

cpp 复制代码
// variadic_params.cpp -- 可变参数
#include <iostream>
#include <initializer_list>
#include <string>

// 方式1:initializer_list(C++11,推荐,类型相同)
double average(std::initializer_list<double> il)
{
    double sum = 0;
    for (double val : il)
        sum += val;
    return il.size() > 0 ? sum / il.size() : 0;
}

void printAll(std::initializer_list<std::string> words)
{
    for (const auto& w : words)
        std::cout << w << " ";
    std::cout << std::endl;
}

// 方式2:变参模板(C++11,类型可不同)
template <typename T>
void print(T t)
{
    std::cout << t << std::endl;
}

template <typename T, typename... Args>
void print(T first, Args... rest)
{
    std::cout << first << " ";
    print(rest...);   // 递归调用
}

int main()
{
    using namespace std;

    // initializer_list
    cout << "平均值:" << average({1.0, 2.0, 3.0, 4.0, 5.0}) << endl;
    printAll({"Hello", "World", "C++"});

    // 变参模板
    print(1, 2.5, "Hello", 'A');

    return 0;
}

6.3 返回类型和 return 语句

6.3.1 无返回值函数

cpp 复制代码
// void_return.cpp -- void函数的return
#include <iostream>

void swap(int& a, int& b)
{
    if (a == b)
        return;   // 提前返回(不需要交换)
    int temp = a;
    a = b;
    b = temp;
}

void printGrade(int score)
{
    if (score < 0 || score > 100)
    {
        std::cout << "无效成绩" << std::endl;
        return;   // 提前返回
    }

    if (score >= 90)      std::cout << "A" << std::endl;
    else if (score >= 80) std::cout << "B" << std::endl;
    else if (score >= 70) std::cout << "C" << std::endl;
    else if (score >= 60) std::cout << "D" << std::endl;
    else                  std::cout << "F" << std::endl;
}

int main()
{
    int a = 5, b = 10;
    swap(a, b);
    std::cout << "a=" << a << " b=" << b << std::endl;

    printGrade(85);
    printGrade(-5);

    return 0;
}

6.3.2 有返回值函数

cpp 复制代码
// return_value.cpp -- 有返回值函数
#include <iostream>
#include <string>
#include <vector>

// 返回局部变量的值(拷贝)
std::string makeGreeting(const std::string& name)
{
    std::string result = "Hello, " + name + "!";
    return result;   // 返回局部变量的拷贝(安全)
}

// ❌ 不能返回局部变量的引用!
// const std::string& badReturn()
// {
//     std::string local = "Hello";
//     return local;   // 危险!local在函数结束后销毁
// }

// ✅ 可以返回引用(引用的对象在调用者作用域内)
const std::string& getFirst(const std::vector<std::string>& v)
{
    return v[0];   // 安全:v 在调用者作用域内
}

// 返回类型为引用,可以作为左值
int& getElement(std::vector<int>& v, int i)
{
    return v[i];
}

// 列表初始化返回值(C++11)
std::vector<std::string> makeList()
{
    return {"Hello", "World", "C++"};   // 列表初始化
}

int main()
{
    using namespace std;

    cout << makeGreeting("Alice") << endl;

    vector<string> words = {"first", "second", "third"};
    cout << getFirst(words) << endl;

    vector<int> nums = {1, 2, 3, 4, 5};
    getElement(nums, 2) = 99;   // 返回引用,可以作为左值
    cout << "nums[2] = " << nums[2] << endl;   // 99

    auto list = makeList();
    for (const auto& s : list)
        cout << s << " ";
    cout << endl;

    return 0;
}

6.3.3 递归函数

cpp 复制代码
// recursion.cpp -- 递归函数
#include <iostream>
#include <string>

// 递归:函数调用自身
// 必须有基准情形(终止条件)

// 阶乘
long long factorial(int n)
{
    if (n <= 1) return 1;          // 基准情形
    return n * factorial(n - 1);   // 递归情形
}

// 斐波那契数列
int fibonacci(int n)
{
    if (n <= 0) return 0;
    if (n == 1) return 1;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

// 二分查找(递归版本)
int binarySearch(const int arr[], int low, int high, int target)
{
    if (low > high) return -1;   // 基准情形:未找到

    int mid = low + (high - low) / 2;
    if (arr[mid] == target) return mid;
    if (arr[mid] > target)
        return binarySearch(arr, low, mid - 1, target);
    else
        return binarySearch(arr, mid + 1, high, target);
}

// 打印缩进树形结构
void printTree(const std::string& name, int depth = 0)
{
    for (int i = 0; i < depth; i++)
        std::cout << "  ";
    std::cout << name << std::endl;
}

int main()
{
    using namespace std;

    // 阶乘
    for (int i = 0; i <= 10; i++)
        cout << i << "! = " << factorial(i) << endl;

    // 斐波那契
    cout << "\n斐波那契数列:";
    for (int i = 1; i <= 10; i++)
        cout << fibonacci(i) << " ";
    cout << endl;

    // 二分查找
    int arr[] = {1, 3, 5, 7, 9, 11, 13, 15};
    int n = sizeof(arr) / sizeof(arr[0]);
    int idx = binarySearch(arr, 0, n - 1, 7);
    cout << "\n查找7:位置 " << idx << endl;

    return 0;
}

6.4 函数重载

cpp 复制代码
// overloading.cpp -- 函数重载
#include <iostream>
#include <string>

// 重载:同名函数,不同参数列表
// 编译器根据实参类型选择正确的版本

void print(int i)
{
    std::cout << "int: " << i << std::endl;
}

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

void print(const std::string& s)
{
    std::cout << "string: " << s << std::endl;
}

void print(int i, int j)
{
    std::cout << "two ints: " << i << ", " << j << std::endl;
}

// 重载与 const
void process(int& r)
{
    std::cout << "非const引用" << std::endl;
}

void process(const int& r)
{
    std::cout << "const引用" << std::endl;
}

// ❌ 以下不构成重载(仅返回类型不同)
// int    getValue() { return 1; }
// double getValue() { return 1.0; }   // 错误!

// ❌ 顶层const不构成重载
// void func(int i)  {}
// void func(const int i) {}   // 错误!重复定义

int main()
{
    using namespace std;

    print(42);           // 调用 print(int)
    print(3.14);         // 调用 print(double)
    print(string("Hi")); // 调用 print(string)
    print(1, 2);         // 调用 print(int, int)

    int x = 5;
    const int cx = 10;
    process(x);    // 调用非const版本
    process(cx);   // 调用const版本
    process(42);   // 调用const版本(字面值绑定const引用)

    return 0;
}

6.5 特殊用途语言特性

6.5.1 默认实参

cpp 复制代码
// default_args.cpp -- 默认实参
#include <iostream>
#include <string>

// 默认实参:从右向左设置
void createWindow(int width = 800,
                  int height = 600,
                  const std::string& title = "My Window")
{
    std::cout << "创建窗口:" << width << "x" << height
              << " 标题:" << title << std::endl;
}

// 默认实参通常在函数声明中指定
std::string format(double val, int precision = 2, char sep = '.');

int main()
{
    using namespace std;

    createWindow();                          // 使用所有默认值
    createWindow(1024);                      // 指定宽度
    createWindow(1024, 768);                 // 指定宽高
    createWindow(1920, 1080, "Full HD");     // 全部指定

    // ⚠️ 不能跳过中间的参数
    // createWindow(, , "Title");   // ❌ 错误!

    return 0;
}

std::string format(double val, int precision, char sep)
{
    // 简化实现
    return std::to_string(val);
}

6.5.2 内联函数

cpp 复制代码
// inline_function.cpp -- 内联函数
#include <iostream>

// inline:建议编译器将函数调用替换为函数体
// 适合短小、频繁调用的函数

inline int max(int a, int b)
{
    return (a > b) ? a : b;
}

inline bool isPositive(double x)
{
    return x > 0;
}

inline int square(int x)
{
    return x * x;
}

// 内联函数通常定义在头文件中
// 因为编译器需要看到函数定义才能内联展开

int main()
{
    using namespace std;

    // 编译器可能将 max(3, 5) 展开为 (3 > 5) ? 3 : 5
    cout << "max(3, 5) = " << max(3, 5) << endl;
    cout << "isPositive(-1) = " << boolalpha << isPositive(-1) << endl;
    cout << "square(7) = " << square(7) << endl;

    // 内联函数 vs 宏
    // 宏:#define MAX(a,b) ((a)>(b)?(a):(b))
    // 问题:MAX(x++, y++) 会导致 x 或 y 被递增两次!
    // 内联函数没有这个问题

    int x = 5, y = 3;
    cout << "max(x++, y++) = " << max(x++, y++) << endl;
    cout << "x=" << x << " y=" << y << endl;   // x=6, y=4(各递增一次)

    return 0;
}

6.5.3 constexpr 函数

cpp 复制代码
// constexpr_function.cpp -- constexpr函数
#include <iostream>
#include <array>

// constexpr 函数:可以在编译时求值
// 函数体只能包含一条 return 语句(C++11)
// C++14 放宽了限制

constexpr int factorial(int n)
{
    return n <= 1 ? 1 : n * factorial(n - 1);
}

constexpr double circleArea(double r)
{
    return 3.14159265358979 * r * r;
}

// C++14:constexpr 函数可以有更复杂的函数体
constexpr int fibonacci(int n)
{
    if (n <= 0) return 0;
    if (n == 1) return 1;
    int a = 0, b = 1;
    for (int i = 2; i <= n; i++)
    {
        int c = a + b;
        a = b;
        b = c;
    }
    return b;
}

int main()
{
    using namespace std;

    // 编译时计算
    constexpr int f5 = factorial(5);   // 编译时:120
    constexpr double area = circleArea(5.0);

    cout << "5! = " << f5 << endl;
    cout << "圆面积(r=5) = " << area << endl;

    // 用于数组大小(必须是编译时常量)
    constexpr int SIZE = factorial(4);   // 24
    array<int, SIZE> arr;
    cout << "数组大小:" << arr.size() << endl;

    // 运行时也可以调用
    int n;
    cin >> n;
    cout << n << "! = " << factorial(n) << endl;

    return 0;
}

6.6 函数匹配

cpp 复制代码
// function_matching.cpp -- 函数匹配规则
#include <iostream>

void f(int)    { std::cout << "f(int)" << std::endl; }
void f(double) { std::cout << "f(double)" << std::endl; }
void f(int, int) { std::cout << "f(int,int)" << std::endl; }

int main()
{
    using namespace std;

    f(42);       // 精确匹配:f(int)
    f(3.14);     // 精确匹配:f(double)
    f(42, 0);    // 精确匹配:f(int,int)

    // 类型提升
    f(true);     // bool → int:f(int)
    f('a');      // char → int:f(int)

    // 标准转换
    // f(42L);   // long → int 或 long → double,二义性!

    // 函数匹配步骤:
    // 1. 确定候选函数(同名函数)
    // 2. 确定可行函数(参数数量和类型匹配)
    // 3. 寻找最佳匹配(精确匹配 > 提升 > 标准转换)

    return 0;
}

6.7 函数指针

cpp 复制代码
// function_pointer.cpp -- 函数指针
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

// 函数指针:指向函数的指针
// 声明:返回类型 (*指针名)(参数类型列表)

bool lengthCompare(const std::string& s1, const std::string& s2)
{
    return s1.size() < s2.size();
}

int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }

// 函数指针作为参数
void applyAndPrint(int a, int b, int (*op)(int, int))
{
    std::cout << "结果:" << op(a, b) << std::endl;
}

// 返回函数指针
int (*getOperation(char op))(int, int)
{
    switch (op)
    {
        case '+': return add;
        case '-': return sub;
        case '*': return mul;
        default:  return nullptr;
    }
}

// 使用 using 简化函数指针类型
using BinaryOp = int(*)(int, int);
using Comparator = bool(*)(const std::string&, const std::string&);

int main()
{
    using namespace std;

    // 声明函数指针
    bool (*pf)(const string&, const string&) = lengthCompare;

    // 调用函数指针
    cout << boolalpha;
    cout << pf("hello", "hi") << endl;   // false(hello更长)

    // 函数名可以直接赋给函数指针(自动取地址)
    pf = &lengthCompare;   // 等价于上面

    // 用于排序
    vector<string> words = {"banana", "apple", "cherry", "fig"};
    sort(words.begin(), words.end(), pf);   // 按长度排序
    for (const auto& w : words) cout << w << " ";
    cout << endl;

    // 函数指针作为参数
    applyAndPrint(10, 3, add);
    applyAndPrint(10, 3, sub);
    applyAndPrint(10, 3, mul);

    // 返回函数指针
    BinaryOp op = getOperation('+');
    if (op) cout << "10 + 3 = " << op(10, 3) << endl;

    // 函数指针数组
    BinaryOp ops[] = {add, sub, mul};
    string opNames[] = {"add", "sub", "mul"};
    for (int i = 0; i < 3; i++)
        cout << opNames[i] << "(10, 3) = " << ops[i](10, 3) << endl;

    return 0;
}

6.8 综合示例:计算器

cpp 复制代码
// calculator.cpp -- 综合示例:函数式计算器
#include <iostream>
#include <string>
#include <map>
#include <functional>
#include <stdexcept>
#include <cmath>
#include <iomanip>

// 使用 std::function 存储各种可调用对象
using BinaryFunc = std::function<double(double, double)>;
using UnaryFunc  = std::function<double(double)>;

// 二元运算
double safeDiv(double a, double b)
{
    if (b == 0) throw std::runtime_error("除数不能为零");
    return a / b;
}

double safeMod(double a, double b)
{
    if (b == 0) throw std::runtime_error("模数不能为零");
    return std::fmod(a, b);
}

double power(double base, double exp)
{
    return std::pow(base, exp);
}

// 一元运算
double mysqrt(double x)
{
    if (x < 0) throw std::domain_error("不能对负数开方");
    return std::sqrt(x);
}

// 计算器类
class Calculator
{
private:
    std::map<std::string, BinaryFunc> binaryOps;
    std::map<std::string, UnaryFunc>  unaryOps;
    double memory = 0;   // 内存存储

public:
    Calculator()
    {
        // 注册二元运算
        binaryOps["+"]   = [](double a, double b) { return a + b; };
        binaryOps["-"]   = [](double a, double b) { return a - b; };
        binaryOps["*"]   = [](double a, double b) { return a * b; };
        binaryOps["/"]   = safeDiv;
        binaryOps["%"]   = safeMod;
        binaryOps["pow"] = power;
        binaryOps["max"] = [](double a, double b) { return std::max(a, b); };
        binaryOps["min"] = [](double a, double b) { return std::min(a, b); };

        // 注册一元运算
        unaryOps["sqrt"] = mysqrt;
        unaryOps["abs"]  = [](double x) { return std::abs(x); };
        unaryOps["sin"]  = [](double x) { return std::sin(x); };
        unaryOps["cos"]  = [](double x) { return std::cos(x); };
        unaryOps["log"]  = [](double x) {
            if (x <= 0) throw std::domain_error("log的参数必须为正");
            return std::log(x);
        };
    }

    double calculate(double a, const std::string& op, double b)
    {
        auto it = binaryOps.find(op);
        if (it == binaryOps.end())
            throw std::invalid_argument("未知运算符:" + op);
        return it->second(a, b);
    }

    double calculate(const std::string& op, double a)
    {
        auto it = unaryOps.find(op);
        if (it == unaryOps.end())
            throw std::invalid_argument("未知函数:" + op);
        return it->second(a);
    }

    void storeMemory(double val) { memory = val; }
    double recallMemory() const  { return memory; }
};

int main()
{
    using namespace std;

    Calculator calc;

    // 测试二元运算
    cout << "===== 二元运算 =====" << endl;
    struct TestCase { double a; string op; double b; };
    vector<TestCase> tests = {
        {10, "+", 3}, {10, "-", 3}, {10, "*", 3},
        {10, "/", 3}, {10, "%", 3}, {2, "pow", 10},
        {10, "max", 20}, {10, "min", 20}
    };

    for (const auto& t : tests)
    {
        try
        {
            double result = calc.calculate(t.a, t.op, t.b);
            cout << fixed << setprecision(4)
                 << t.a << " " << t.op << " " << t.b
                 << " = " << result << endl;
        }
        catch (const exception& e)
        {
            cout << "错误:" << e.what() << endl;
        }
    }

    // 测试一元运算
    cout << "\n===== 一元运算 =====" << endl;
    struct UnaryTest { string op; double val; };
    vector<UnaryTest> utests = {
        {"sqrt", 16}, {"abs", -5}, {"sin", 0},
        {"cos", 0}, {"log", 2.718}
    };

    for (const auto& t : utests)
    {
        try
        {
            double result = calc.calculate(t.op, t.val);
            cout << t.op << "(" << t.val << ") = "
                 << fixed << setprecision(4) << result << endl;
        }
        catch (const exception& e)
        {
            cout << "错误:" << e.what() << endl;
        }
    }

    // 测试错误处理
    cout << "\n===== 错误处理 =====" << endl;
    try { calc.calculate(10, "/", 0); }
    catch (const exception& e) { cout << "捕获:" << e.what() << endl; }

    try { calc.calculate("sqrt", -4); }
    catch (const exception& e) { cout << "捕获:" << e.what() << endl; }

    return 0;
}

📝 第6章知识点总结

知识点 核心要点
函数定义 返回类型+函数名+参数列表+函数体,局部变量在函数结束时销毁
局部静态变量 static 修饰,只初始化一次,程序结束时销毁,保留上次的值
函数声明 原型告诉编译器接口,可以在定义前调用,通常放在头文件
传值参数 函数接收副本,修改不影响原变量
传引用参数 函数可以修改原变量,避免大对象拷贝
const引用 只读参数,可以接受字面值和右值,推荐用于大对象
数组参数 退化为指针,必须同时传大小;数组引用保留大小信息
可变参数 initializer_list(同类型)或变参模板(不同类型)
返回引用 不能返回局部变量的引用,可以返回引用参数或静态变量
递归 必须有基准情形,每次调用向基准靠近
函数重载 同名不同参数,返回类型不同不构成重载,顶层const不区分
默认实参 从右向左设置,调用时可省略右侧参数
内联函数 建议编译器展开,适合短小频繁调用,通常定义在头文件
constexpr函数 编译时求值,可用于数组大小等常量表达式场景
函数指针 返回类型(*ptr)(参数类型),可作为参数和返回值
相关推荐
dnbug Blog1 小时前
C语言 简介
c语言·开发语言
码上有光1 小时前
c++:多态
java·jvm·c++·多态·多态原理
Lumbrologist1 小时前
【C++】零基础入门 · 第 18 节:互斥锁与线程同步
java·开发语言·c++
tangchao340勤奋的老年?1 小时前
C++ OpenGL显示地图
c++·opengl
炸炸鱼.1 小时前
Zabbix企业级高级应用:从自动化监控到自定义告警完全指南
开发语言·php
I Promise341 小时前
C++ 多线程编程:从入门到实战
开发语言·c++
kkeeper~1 小时前
0基础C语言积跬步之自定义类型联合和枚举
c语言·开发语言·算法
邪修king1 小时前
C++map_set封装 : 红黑树底层迭代器以及仿函数的运用
android·c语言·数据结构·c++·b树
七夜zippoe1 小时前
DolphinDB自定义函数:UDF开发指南
开发语言·python·自定义函数·udf·dolphindb