【C++ Primer】第六章:函数

这一章主要讲解C++中的函数,这是构建模块化、可重用代码的基础。

6.1 函数基础

函数定义和调用

cpp 复制代码
#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

// 1. 函数声明(函数原型)
int factorial(int n);
void printMessage(const string& message);

// 2. 函数定义
int factorial(int n) {
    int result = 1;
    for (int i = 1; i <= n; ++i) {
        result *= i;
    }
    return result;
}

void printMessage(const string& message) {
    cout << "消息: " << message << endl;
}

// 3. 参数列表可以为空
void sayHello() {
    cout << "Hello, World!" << endl;
}

int main()
{
    // 函数调用
    sayHello();
    printMessage("欢迎学习C++函数!");
    
    int num = 5;
    int result = factorial(num);
    cout << num << "! = " << result << endl;
    
    return 0;
}

局部对象和作用域

cpp 复制代码
#include <iostream>

using std::cout;
using std::endl;

// 全局变量
int globalVar = 100;

void demonstrateScope() {
    // 局部变量
    int localVar = 50;
    static int staticVar = 0;  // 静态局部变量
    
    cout << "函数内 - 局部变量: " << localVar << endl;
    cout << "函数内 - 静态变量: " << staticVar << endl;
    cout << "函数内 - 全局变量: " << globalVar << endl;
    
    localVar++;
    staticVar++;
    globalVar++;
}

int main()
{
    cout << "第一次调用:" << endl;
    demonstrateScope();
    
    cout << "\n第二次调用:" << endl;
    demonstrateScope();
    
    cout << "\n主函数内 - 全局变量: " << globalVar << endl;
    // cout << localVar << endl;  // 错误!localVar不可访问
    
    return 0;
}

6.2 参数传递

传值参数

cpp 复制代码
#include <iostream>

using std::cout;
using std::endl;

// 传值参数:形参是实参的副本
void passByValue(int x) {
    x = x * 2;  // 只修改副本,不影响原值
    cout << "函数内 x = " << x << endl;
}

// 指针参数:可以修改指向的对象
void passByPointer(int* ptr) {
    if (ptr) {  // 检查指针是否有效
        *ptr = *ptr * 2;  // 修改指针指向的值
        cout << "函数内 *ptr = " << *ptr << endl;
    }
}

int main()
{
    int a = 10;
    
    cout << "传值调用前: a = " << a << endl;
    passByValue(a);
    cout << "传值调用后: a = " << a << endl;
    
    cout << "\n传指针调用前: a = " << a << endl;
    passByPointer(&a);
    cout << "传指针调用后: a = " << a << endl;
    
    return 0;
}

传引用参数

cpp 复制代码
#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

// 传引用参数:形参是实参的别名
void passByReference(int& ref) {
    ref = ref * 2;  // 直接修改原值
    cout << "函数内 ref = " << ref << endl;
}

// 使用引用避免拷贝大型对象
void printLargeObject(const string& str) {
    cout << "字符串: " << str << endl;
    // str[0] = 'H';  // 错误!const引用不能修改
}

// 使用引用返回多个值
void calculate(int a, int b, int& sum, int& product) {
    sum = a + b;
    product = a * b;
}

// 引用参数的重载
void processValue(int x) {
    cout << "处理值: " << x << endl;
}

void processValue(int& x) {
    cout << "处理引用: " << x << endl;
    x++;  // 可以修改原值
}

int main()
{
    int num = 5;
    
    cout << "传引用调用前: num = " << num << endl;
    passByReference(num);
    cout << "传引用调用后: num = " << num << endl;
    
    // 使用引用返回多个值
    int x = 10, y = 20;
    int sum, product;
    calculate(x, y, sum, product);
    cout << x << " + " << y << " = " << sum << endl;
    cout << x << " * " << y << " = " << product << endl;
    
    // 引用重载
    int value = 100;
    processValue(value);     // 调用引用版本
    processValue(value + 1); // 调用传值版本(临时对象)
    
    return 0;
}

const参数和返回值

cpp 复制代码
#include <iostream>
#include <vector>

using std::cout;
using std::endl;
using std::vector;

// const引用参数:避免拷贝,同时防止修改
bool findValue(const vector<int>& vec, int value) {
    for (const auto& elem : vec) {
        if (elem == value) {
            return true;
        }
    }
    return false;
}

// 返回const引用(要确保引用对象在函数返回后仍然存在)
const string& getLongerString(const string& s1, const string& s2) {
    return s1.size() > s2.size() ? s1 : s2;
}

// 返回指向常量的指针
const int* findMaxElement(const int arr[], size_t size) {
    if (size == 0) return nullptr;
    
    const int* maxPtr = &arr[0];
    for (size_t i = 1; i < size; ++i) {
        if (arr[i] > *maxPtr) {
            maxPtr = &arr[i];
        }
    }
    return maxPtr;
}

int main()
{
    vector<int> numbers = {1, 3, 5, 7, 9};
    int target = 5;
    
    if (findValue(numbers, target)) {
        cout << "找到 " << target << endl;
    } else {
        cout << "未找到 " << target << endl;
    }
    
    string str1 = "hello";
    string str2 = "worldwide";
    const string& longer = getLongerString(str1, str2);
    cout << "较长的字符串: " << longer << endl;
    
    int arr[] = {3, 1, 4, 1, 5, 9, 2, 6};
    const int* maxElement = findMaxElement(arr, 8);
    if (maxElement) {
        cout << "最大元素: " << *maxElement << endl;
    }
    
    return 0;
}

6.3 返回类型和return语句

值返回

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>

using std::cout;
using std::endl;
using std::string;
using std::vector;

// 返回内置类型
int square(int x) {
    return x * x;  // 返回值
}

// 返回类类型(可能涉及拷贝)
string getGreeting(bool formal) {
    if (formal) {
        return "Good day";  // 返回临时string对象
    } else {
        return "Hi";        // 返回另一个临时string对象
    }
}

// 返回引用(必须确保引用对象仍然存在)
const int& getElement(const vector<int>& vec, size_t index) {
    if (index >= vec.size()) {
        throw std::out_of_range("索引越界");
    }
    return vec[index];  // 返回const引用
}

// 返回列表(C++11)
vector<int> getNumbers() {
    return {1, 2, 3, 4, 5};  // 返回初始化列表
}

// 主函数返回类型
int main() {
    cout << "5的平方: " << square(5) << endl;
    cout << "正式问候: " << getGreeting(true) << endl;
    
    vector<int> nums = {10, 20, 30};
    const int& elem = getElement(nums, 1);
    cout << "第二个元素: " << elem << endl;
    
    auto numbers = getNumbers();
    cout << "数字列表: ";
    for (auto n : numbers) {
        cout << n << " ";
    }
    cout << endl;
    
    return 0;  // 主函数返回0表示成功
}

引用返回

cpp 复制代码
#include <iostream>
#include <vector>

using std::cout;
using std::endl;
using std::vector;

// 返回非常量引用,允许修改
int& getElement(vector<int>& vec, size_t index) {
    return vec[index];  // 返回引用,可以修改原元素
}

// 错误的引用返回:返回局部变量的引用
// int& badFunction() {
//     int local = 42;
//     return local;  // 错误!local在函数结束时被销毁
// }

// 返回静态局部变量的引用
int& getCounter() {
    static int counter = 0;  // 静态变量在程序结束时才销毁
    return counter;
}

int main()
{
    vector<int> numbers = {1, 2, 3, 4, 5};
    
    cout << "修改前: ";
    for (auto n : numbers) {
        cout << n << " ";
    }
    cout << endl;
    
    // 通过返回的引用修改元素
    getElement(numbers, 2) = 100;
    
    cout << "修改后: ";
    for (auto n : numbers) {
        cout << n << " ";
    }
    cout << endl;
    
    // 使用计数器
    getCounter() = 10;
    cout << "计数器: " << getCounter() << endl;
    
    getCounter() += 5;
    cout << "计数器: " << getCounter() << endl;
    
    return 0;
}

6.4 函数重载

cpp 复制代码
#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

// 函数重载:同名函数,不同参数列表

// 1. 参数类型不同
void print(int value) {
    cout << "整数: " << value << endl;
}

void print(double value) {
    cout << "浮点数: " << value << endl;
}

void print(const string& value) {
    cout << "字符串: " << value << endl;
}

// 2. 参数数量不同
void show() {
    cout << "无参数" << endl;
}

void show(int a) {
    cout << "一个参数: " << a << endl;
}

void show(int a, int b) {
    cout << "两个参数: " << a << ", " << b << endl;
}

// 3. 参数类型修饰不同
void process(int x) {  // 传值
    cout << "传值: " << x << endl;
}

void process(int& x) {  // 传引用
    cout << "传引用: " << x << endl;
    x++;
}

void process(const int& x) {  // 传常量引用
    cout << "传常量引用: " << x << endl;
}

// 4. 默认参数(与重载相关)
void display(string message, bool newline = true) {
    cout << message;
    if (newline) {
        cout << endl;
    }
}

int main()
{
    // 重载函数调用
    print(42);          // 调用print(int)
    print(3.14);        // 调用print(double)
    print("hello");     // 调用print(const string&)
    
    show();             // 调用show()
    show(10);           // 调用show(int)
    show(20, 30);       // 调用show(int, int)
    
    int num = 5;
    process(num);       // 调用process(int&)
    process(100);       // 调用process(const int&)
    process(static_cast<const int&>(num)); // 调用process(const int&)
    
    // 默认参数
    display("第一行");
    display("第二行", true);
    display("没有换行", false);
    cout << "这是新的一行" << endl;
    
    return 0;
}

6.5 特殊用途语言特性

默认实参

cpp 复制代码
#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

// 默认实参:在参数列表中指定默认值
void createWindow(string title = "Untitled", 
                  int width = 800, 
                  int height = 600, 
                  bool fullscreen = false) {
    cout << "创建窗口:" << endl;
    cout << "  标题: " << title << endl;
    cout << "  宽度: " << width << endl;
    cout << "  高度: " << height << endl;
    cout << "  全屏: " << (fullscreen ? "是" : "否") << endl;
    cout << endl;
}

// 默认实参只能从右向左提供
void func(int a, int b = 10, int c = 20) {
    cout << "a=" << a << ", b=" << b << ", c=" << c << endl;
}

// 错误:默认实参不在最后
// void badFunc(int a = 5, int b) { }  // 编译错误

int main()
{
    // 使用不同数量的参数调用
    createWindow();                            // 使用所有默认值
    createWindow("My App");                    // 只提供标题
    createWindow("Game", 1024);                // 提供标题和宽度
    createWindow("Fullscreen App", 1920, 1080, true); // 提供所有参数
    
    func(1);        // a=1, b=10, c=20
    func(1, 2);     // a=1, b=2, c=20
    func(1, 2, 3);  // a=1, b=2, c=3
    
    return 0;
}

内联函数和constexpr函数

cpp 复制代码
#include <iostream>

using std::cout;
using std::endl;

// 内联函数:建议编译器在调用点展开函数体
inline int max(int a, int b) {
    return a > b ? a : b;
}

// constexpr函数:用于常量表达式,在编译时求值;
// 但是:如果编译时n的值还不知道,就会在运行是求值。
constexpr int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}

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

// 调试用的内联函数
inline void debugPrint(const char* message) {
#ifdef DEBUG
    cout << "[DEBUG] " << message << endl;
#endif
}

int main()
{
    // 内联函数调用
    int a = 5, b = 10;
    cout << "最大值: " << max(a, b) << endl;
    
    // constexpr函数调用
    constexpr int size = factorial(5);  // 编译时计算
    cout << "5! = " << size << endl;
    
    int arr[square(3)];  // 数组大小在编译时确定
    cout << "数组大小: " << sizeof(arr) / sizeof(arr[0]) << endl;
    
    // 运行时也可以调用constexpr函数
    int x = 4;
    cout << x << "! = " << factorial(x) << endl;
    
    debugPrint("程序启动");
    
    return 0;
}

6.6 函数匹配

cpp 复制代码
#include <iostream>

using std::cout;
using std::endl;

void f() {
    cout << "f()" << endl;
}

void f(int a) {
    cout << "f(int)" << endl;
}

void f(int a, int b) {
    cout << "f(int, int)" << endl;
}

void f(double a, double b = 3.14) {
    cout << "f(double, double)" << endl;
}

void example(int a, double b) {
    cout << "example(int, double)" << endl;
}

void example(double a, int b) {
    cout << "example(double, int)" << endl;
}

int main()
{
    // 精确匹配
    f(5);           // f(int)
    
    // 类型转换匹配
    f(5.0);         // f(double, double) - double到int需要转换
    
    // 二义性调用
    // f(5, 5.0);    // 错误!二义性:f(int, int) 或 f(double, double)
    
    // 通过强制转换解决二义性
    f(static_cast<double>(5), 5.0);  // f(double, double)
    
    // 另一个二义性例子
    // example(5, 5); // 错误!二义性:需要int->double或double->int转换
    
    // 默认参数的影响
    f(5.0);         // 调用f(double, double),使用默认参数
    
    return 0;
}

6.7 函数指针(含函数指针数组)

cpp 复制代码
#include <iostream>
#include <vector>
#include <cmath>

using std::cout;
using std::endl;
using std::vector;

// 普通函数
int add(int a, int b) {
    return a + b;
}

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

double squareRoot(double x) {
    return std::sqrt(x);
}

// 使用函数指针作为参数
void processNumbers(const vector<int>& numbers, int (*operation)(int, int), int init) {
    int result = init;
    for (size_t i = 0; i < numbers.size(); ++i) {
        result = operation(result, numbers[i]);
    }
    cout << "处理结果: " << result << endl;
}

// 使用typedef/using简化函数指针类型
using MathOperation = int(*)(int, int);

void calculate(MathOperation op, int a, int b) {
    int result = op(a, b);
    cout << "计算结果: " << result << endl;
}

int main()
{
    // 函数指针声明和赋值
    int (*funcPtr)(int, int);
    
    funcPtr = add;  // 指向add函数
    cout << "加法: " << funcPtr(5, 3) << endl;
    
    funcPtr = multiply;  // 指向multiply函数
    cout << "乘法: " << funcPtr(5, 3) << endl;
    
    // 函数指针数组
    int (*operations[])(int, int) = {add, multiply};
    cout << "操作0: " << operations[0](5, 3) << endl;  // add
    cout << "操作1: " << operations[1](5, 3) << endl;  // multiply
    
    // 函数指针作为参数
    vector<int> numbers = {1, 2, 3, 4, 5};
    processNumbers(numbers, add, 0);        // 求和
    processNumbers(numbers, multiply, 1);   // 求积
    
    // 使用类型别名
    calculate(add, 10, 20);
    calculate(multiply, 10, 20);
    
    return 0;
}

📝 第六章关键要点总结

  1. 参数传递方式

    • 传值:创建副本,不影响原值
    • 传引用:操作原对象,避免拷贝
    • 传const引用:只读访问,高效安全
  2. 返回类型选择

    • 值返回:返回副本
    • 引用返回:返回别名,要确保对象生命周期
    • 不要返回局部变量的引用或指针
  3. 函数设计原则

    • 单一职责:一个函数只做一件事
    • 合理重载:参数列表应该明显不同
    • 使用默认参数简化接口
  4. 特殊函数

    • 内联函数:小型频繁调用的函数
    • constexpr函数:编译时求值的函数

这一章是C++编程的重要基础,函数是代码重用的关键。多练习函数的设计和使用,为后续面向对象编程打下坚实基础!

相关推荐
杨云强2 小时前
离散积分,相同表达式数组和公式
算法
地平线开发者2 小时前
征程 6 | BPU trace 简介与实操
算法·自动驾驶
满天星83035772 小时前
【C++】AVL树的模拟实现
开发语言·c++·算法·stl
weixin_456904273 小时前
基于.NET Framework 4.0的串口通信
开发语言·c#·.net
ss2733 小时前
手写MyBatis第107弹:@MapperScan原理与SqlSessionTemplate线程安全机制
java·开发语言·后端·mybatis
Lris-KK3 小时前
力扣Hot100--94.二叉树的中序遍历、144.二叉树的前序遍历、145.二叉树的后序遍历
python·算法·leetcode
Mr_WangAndy3 小时前
C++设计模式_行为型模式_责任链模式Chain of Responsibility
c++·设计模式·责任链模式·行为型模式
麦麦鸡腿堡4 小时前
Java的动态绑定机制(重要)
java·开发语言·算法
时间之里4 小时前
【c++】:Lambda 表达式介绍和使用
开发语言·c++