解码C++基础——从C到C++

C++ 概述

C++ 是多范式编程语言,支持过程化编程、面向对象编程(OOP)和泛型编程,兼容 C 语言且增强了类型检查与功能特性。核心优势包括封装 / 继承 / 多态、模板与泛型编程、异常处理、标准模板库(STL)。

最简 C++ 程序与结构

#include <iostream>  // 包含C++标准输入输出头文件
using namespace std; // 导入std命名空间,简化符号使用

int main() {
    cout << "Hello World!" << endl; // 标准输出流输出字符串
    return 0; // 程序正常结束返回0
}

程序核心结构:

  • 头文件:提供函数 / 类的声明,C++ 标准头文件无.h后缀(如<iostream>),兼容 C 头文件(如<stdio.h>可写为<cstdio>)。
  • 命名空间:解决符号命名冲突,std是 C++ 标准库的命名空间。
  • main 函数:程序入口,返回int类型表示运行状态(0 为正常)。

名字空间(Namespace)

基本概念

名字空间是 C++ 中用于划分自定义作用域的机制,核心目的是解决大型程序开发中全局符号命名冲突问题(如不同库、模块间的变量、函数重名)。不同名字空间内的同名符号相互独立,互不干扰,能有效管理代码模块的边界。

定义与使用

定义名字空间

名字空间通过namespace关键字定义,内部可包含变量、函数、类、甚至嵌套名字空间等元素:

// 定义名字空间:数学工具模块
namespace mathUtils {
    int baseValue = 1;          // 基础数值(语义化命名)
    float calculateHalf(int inputNum) { // 计算输入值的一半
        return inputNum / 2.0f;
    }
}

访问名字空间符号

需通过 ** 作用域解析符::** 指定符号所属的名字空间,格式为名字空间名::符号名

#include <iostream>// 头文件中声明名字空间符号(遵循声明-定义分离原则)
namespace mathUtils {
    extern int baseValue;
    extern float calculateHalf(int inputNum);
}

int main() {
    std::cout << mathUtils::baseValue << std::endl;        // 输出1
    std::cout << mathUtils::calculateHalf(8) << std::endl; // 输出4
    return 0;
}

using 语句(简化访问)

频繁使用名字空间名::符号名会增加代码冗余,可通过using语句简化访问:

导入整个名字空间

using namespace mathUtils; // 导入mathUtils中所有符号
int main() {
    std::cout << baseValue << std::endl;        // 直接访问baseValue
    std::cout << calculateHalf(8) << std::endl; // 直接访问calculateHalf
    return 0;
}

导入指定符号

避免导入全部符号导致的 “名字空间污染”(未使用的符号成为潜在冲突源):

using mathUtils::baseValue;    // 仅导入baseValue
using mathUtils::calculateHalf;// 仅导入calculateHalf
int main() {
    std::cout << baseValue << std::endl;        // 合法
    std::cout << calculateHalf(8) << std::endl; // 合法
    return 0;
}

进阶语法

内嵌名字空间

名字空间支持嵌套定义,形成层级化的作用域结构,访问时需逐层指定:

namespace project { // 项目根名字空间
    int globalConfig = 1; // 全局配置值
    namespace calculateModule { // 计算模块子空间
        int localConfig = 2;  // 局部配置值
        int resultCode = 100; // 结果状态码
    }
}

int main() {
    std::cout << project::globalConfig << std::endl;                  // 输出1
    std::cout << project::calculateModule::localConfig << std::endl;  // 输出2
    std::cout << project::calculateModule::resultCode << std::endl;   // 输出100
    return 0;
}

名字空间的扩展性

同一名字空间可在不同位置补充定义,最终会合并为一个完整的作用域:

// 初始定义
namespace mathUtils {
    int baseValue = 1;
}
// 扩展定义(新增符号)
namespace mathUtils {
    int maxLimit = 666; // 新增最大值限制
}

int main() {
    std::cout << mathUtils::baseValue << std::endl; // 输出1(原有符号)
    std::cout << mathUtils::maxLimit << std::endl;  // 输出666(新增符号)
    return 0;
}

全局作用域

全局作用域是匿名的,若局部符号与全局符号重名,可通过::符号名访问全局符号:

int globalCounter = 100; // 全局计数器
int main() {
    int globalCounter = 200; // 局部计数器(覆盖全局)
    std::cout << globalCounter << std::endl;   // 输出200(局部)
    std::cout << ::globalCounter << std::endl; // 输出100(全局)
    return 0;
}

实战问题:名字冲突解决

问题场景:自定义函数与标准库函数重名(如max)。解决方案:将自定义函数放入独立名字空间,避免冲突:

#include <iostream>
namespace customMath { // 自定义数学工具空间
    int maxValue; // 存储最大值
    void findMax(float firstNum, float secondNum) { // 寻找两数最大值
        maxValue = firstNum > secondNum ? firstNum : secondNum;
    }
}

int main() {
    customMath::findMax(3.0f, 4.0f); // 明确调用自定义函数
    std::cout << customMath::maxValue << std::endl; // 输出4
    return 0;
}

输入输出流(cin/cout)

C++ 通过标准流对象实现输入输出,比 C 的scanf/printf更安全、易用。

核心流对象

  • cout:标准输出流,输出数据到控制台,支持自动类型推导。
  • cin:标准输入流,从控制台读取数据,自动匹配变量类型。
  • cerr:标准错误流,无缓冲区,直接输出错误信息(用法同cout)。

示例

#include <iostream>
using std::cin;
using std::cout;
using std::endl;

int main() {
    int a;
    double b;
    string s;

    cout << "输入整数、浮点数、字符串:" << endl;
    cin >> a >> b >> s; // 一次性读取多种类型

    cout << "整数:" << a << endl;
    cout << "浮点数:" << b << endl;
    cout << "字符串:" << s << endl;

    cerr << "错误信息示例" << endl; // 错误输出
    return 0;
}

对比 C 输入输出

  • 类型安全cin/cout自动匹配类型,scanf/printf需手动指定格式(易出错)。
  • 扩展性cin/cout支持运算符重载(自定义类型可直接输出),scanf/printf需手动格式化。

数据类型与字符串

基础数据类型

C++ 兼容 C 的所有基础类型(char/short/int/long/float/double),新增bool类型(取值true/false)。

字符串类型(string)

C 语言用char[]/char*管理字符串(需手动处理内存),C++ 提供std::string(自动管理内存,支持动态扩容)。

#include <iostream>
#include <string> // 需包含string头文件
using std::cin;
using std::cout;
using std::string;
using std::endl;

int main() {
    string s1 = "Hello";
    string s2 = "World";
    string s3 = s1 + " " + s2; // 字符串拼接(运算符重载)

    cout << "拼接结果:" << s3 << endl; // 输出:Hello World
    cout << "长度:" << s3.size() << endl; // 输出:11
    cout << "子串:" << s3.substr(0, 5) << endl; // 输出:Hello

    // 比较字符串
    if (s1 == "Hello") {
        cout << "s1等于Hello" << endl;
    }
    return 0;
}

优势

  • 自动内存管理,避免缓冲区溢出(如 C 的strcpy风险)。
  • 支持运算符重载(+拼接、=赋值、==比较等)。

引用机制

概念

引用是已存在变量的别名,与原变量共享内存地址,操作引用等价于操作原变量。

语法与特性

#include <iostream>
using namespace std;

int main() {
    int a = 10;
    int& ref = a; // 定义引用,必须初始化(ref是a的别名)

    ref = 20; // 等价于a=20,a的值变为20
    cout << "a = " << a << endl; // 输出:a = 20
    cout << "ref = " << ref << endl; // 输出:ref = 20
    cout << "a的地址:" << &a << endl;
    cout << "ref的地址:" << &ref << endl; // 与a的地址相同

    // 引用不可重新绑定
    int b = 30;
    ref = b; // 这是赋值,不是重新绑定!a的值变为30,ref仍指向a
    cout << "a = " << a << endl; // 输出:a = 30
    return 0;
}

核心规则

  • 必须初始化:定义引用时必须绑定有效变量(int& ref;非法)。
  • 不可重新绑定:初始化后无法切换指向的变量。
  • 无独立内存:引用是别名,不占用额外内存(编译器优化为指针常量int* const)。
  • 无空引用:必须绑定有效对象(int& ref = NULL;非法)。

引用的应用

函数传参(避免拷贝,提升效率)

/**
 * 交换两个整数的函数
 * @brief 通过引用传参直接操作原变量,避免拷贝
 * @param a 第一个整数的引用
 * @param b 第二个整数的引用
 * @return void 无返回值
 */
void swap(int& a, int& b) {
    int tmp = a;
    a = b;
    b = tmp;
}

int main() {
    int x = 5, y = 10;
    swap(x, y);
    cout << "x = " << x << ", y = " << y << endl; // 输出:x=10,y=5
    return 0;
}

函数返回值(返回全局 / 静态变量的引用)

/**
 * 返回静态变量的引用
 * @brief 静态变量生命周期与程序一致,返回引用安全
 * @return int& 静态变量A的引用
 */
int& getStaticVar() {
    static int A = 10; // 静态变量,存储在全局区
    return A;
}

int main() {
    int& ref = getStaticVar();
    ref = 20; // 修改静态变量的值
    cout << getStaticVar() << endl; // 输出:20
    return 0;
}

注意:避免返回局部变量的引用(局部变量在函数结束后销毁,引用变为 “悬空引用”)。

常量引用(只读访问,兼容临时对象)

/**
 * 打印字符串的函数
 * @brief 常量引用避免拷贝,且可绑定临时对象(如字面量)
 * @param s 字符串的常量引用(只读,不可修改)
 * @return void 无返回值
 */
void print(const string& s) {
    cout << s << endl;
}

int main() {
    print("Hello World"); // 常量引用可绑定临时字符串(合法)
    string str = "Test";
    print(str); // 也可绑定普通字符串
    return 0;
}

引用与指针对比

特性 引用 指针
初始化 必须初始化 可空或后续赋值
语法 无需*/-> *解引用、->访问成员
内存占用 无额外内存 占用内存(32 位 4B,64 位 8B)
安全性 无空引用风险 需检查空指针
多级访问 仅支持一级引用 支持多级指针(如int**

类型转换

C++ 兼容 C 的旧式转换(如(int)f),新增新式类型转换(更安全、可读性更高)。

const_cast(去除 const/volatile 属性)

用途:仅用于指针或引用,去除其const/volatile限定。

#include <iostream>using namespace std;

int main() {
    const int a = 10;
    const int* p = &a;

    // 错误:p是常目标指针,无法修改指向的内容
    // *p = 20;

    // 使用const_cast去除const属性
    int* q = const_cast<int*>(p);
    *q = 20; // 现在可修改(注意:若a是真正的常量,可能触发未定义行为)
    cout << "a = " << a << endl; // 输出:a=20(a为普通const变量时生效)

    // 处理引用
    const int& ref = a;
    // ref = 30; // 错误
    const_cast<int&>(ref) = 30; // 合法
    cout << "a = " << a << endl; // 输出:a=30
    return 0;
}

注意:不能去除变量本身的const(如const int a=10;a本质是常量,强制修改可能崩溃),仅能去除指针 / 引用的const属性。

static_cast(静态类型转换)

用途:用于兼容类型的转换(如数值类型、父类子类指针),编译期检查类型安全性。

#include <iostream>
using namespace std;

int main() {
    float f = 3.14;
    int i = static_cast<int>(f); // 浮点转整型(截断小数)
    cout << "i = " << i << endl; // 输出:i=3

    // 错误:不兼容类型转换,static_cast会报错(比旧式转换安全)
    // int* p = static_cast<int*>(&f);

    // 父类子类转换(上行转换安全)
    class Base {};
    class Derived : public Base {};
    Derived d;
    Base* b = static_cast<Base*>(&d); // 子类转父类(合法)
    return 0;
}

优势:比旧式转换更易识别,且能阻止不兼容类型的转换(如float*int*)。

dynamic_cast(动态类型转换)

用途:仅用于类的继承体系,运行期检查类型(需类有虚函数),实现安全的下行转换(父类转子类)。

reinterpret_cast(重解释转换)

用途:强制转换任意类型的指针 / 引用(风险高,如int*char*),仅用于底层操作。

关键字 auto

auto让编译器自动推导变量类型,简化代码(尤其适用于复杂类型)。

#include <iostream>
#include <vector>
using namespace std;

int main() {
    auto a = 10; // 推导为int
    auto b = 3.14; // 推导为double
    auto c = "Hello"; // 推导为const char*

    vector<int> vec = {1,2,3};
    for (auto it = vec.begin(); it != vec.end(); ++it) { // it推导为vector<int>::iterator
        cout << *it << " ";
    }
    return 0;
}

注意auto变量必须初始化(编译器需通过初始值推导类型)。

命名规范

C++ 无强制命名规范,但通用规则可提升代码可读性:

  • 类名 / 结构体名:大驼峰式(首字母大写),如AppleTreeStudent
  • 变量 / 函数名:小驼峰式(首字母小写),如studentNamecalculateSum
  • 常量名:全大写 + 下划线,如MAX_SIZEPI
  • 命名空间名:小写,如my_space

函数特性

默认参数

函数参数可指定默认值,调用时若未传参则使用默认值。

/**
 * 计算圆锥体体积
 * @brief 高度默认值为1,半径必须传参
 * @param r 圆锥底部半径(无默认值)
 * @param h 圆锥高度(默认值1)
 * @return double 圆锥体积(公式:1/3 * π * r² * h)
 */
double coneVolume(double r, double h = 1.0) {
    const double PI = 3.1415926;
    return (1.0 / 3) * PI * r * r * h;
}

int main() {
    cout << "体积(h=默认):" << coneVolume(2) << endl; // h=1,输出≈4.1888
    cout << "体积(h=3):" << coneVolume(2, 3) << endl; // h=3,输出≈12.5664
    return 0;
}

规则

  • 默认参数必须从右到左连续定义(int func(int a=1, int b);非法)。
  • 声明和定义中默认参数不可重复(声明写默认值,定义不写)。

占位参数

函数参数仅声明类型,无参数名,用于预留接口或兼容旧代码。

/**
 * 占位参数示例
 * @brief 第二个参数为占位符,调用时需传值但函数内无法使用
 * @param a 第一个参数(可用)
 * @param int 第二个参数(占位,无名称)
 * @return void 无返回值
 */
void func(int a, int) {
    cout << "a = " << a << endl;
}

int main() {
    func(10, 20); // 必须传第二个参数(20),但函数内无法访问
    return 0;
}

函数重载

同一作用域内,同名函数的参数类型 / 个数 / 顺序不同,可形成重载(编译器根据参数区分调用)。

合法重载情形

#include <iostream>using namespace std;

// 参数个数不同
void print(int a) {
    cout << "整数:" << a << endl;
}
void print(int a, int b) {
    cout << "两个整数:" << a << "," << b << endl;
}

// 参数类型不同
void print(double a) {
    cout << "浮点数:" << a << endl;
}

// 类方法const属性不同
class MyClass {
public:
    void show() { // 普通方法
        cout << "普通show()" << endl;
    }
    void show() const { // const方法(this指针为const)
        cout << "const show()" << endl;
    }
};

// 普通指针与常目标指针不同
void func(char* p) {
    cout << "普通指针:" << p << endl;
}
void func(const char* p) {
    cout << "常目标指针:" << p << endl;
}

int main() {
    print(10); // 调用print(int)
    print(3.14); // 调用print(double)
    print(10, 20); // 调用print(int,int)

    MyClass obj;
    const MyClass constObj;
    obj.show(); // 调用普通show()
    constObj.show(); // 调用const show()

    char* s = "test";
    const char* cs = "const test";
    func(s); // 调用func(char*)
    func(cs); // 调用func(const char*)
    return 0;
}

非法重载情形

  • 返回值不同int func();double func();无法重载。
  • static 修饰不同static int func();int func();无法重载。
  • 参数 const 修饰不同(非指针 / 引用)void func(int a);void func(const int a);无法重载(参数本质相同)。
  • 引用与值的歧义void func(int a);void func(int& a);调用func(10)时会二义性(10 是常量,无法绑定普通引用)。

堆内存管理

C 语言用malloc/free管理堆内存,C++ 提供new/delete(更安全,支持对象构造 / 析构)。

单个对象的 new/delete

#include <iostream>
using namespace std;

int main() {
    // 动态分配int类型内存,初始化为10
    int* p = new int(10); 
    cout << *p << endl; // 输出:10

    delete p; // 释放内存(必须配对,否则内存泄漏)
    p = nullptr; // 避免悬空指针

    // 动态分配对象
    class Test {
    public:
        Test() { cout << "Test构造" << endl; }
        ~Test() { cout << "Test析构" << endl; }
    };
    Test* t = new Test(); // 调用构造函数
    delete t; // 调用析构函数+释放内存
    return 0;
}

数组的 new []/delete []

int main() {
    // 动态分配int数组(5个元素)
    int* arr = new int[5]{1,2,3,4,5}; // C++11支持初始化
    for (int i=0; i<5; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;

    delete[] arr; // 释放数组(必须用delete[],否则仅释放第一个元素)
    arr = nullptr;
    return 0;
}

对比 malloc/free

  • new/delete是运算符,malloc/free是函数。
  • new自动调用构造函数,delete自动调用析构函数(malloc/free不支持)。
  • new返回对应类型指针(无需强制转换),malloc返回void*(需转换)。

枚举与范围 for 循环

枚举(enum/enum class)

普通枚举

enum Color {
    Red, // 默认0
    Green, // 1
    Blue // 2
};

int main() {
    Color c = Red;
    cout << c << endl; // 输出:0
    return 0;
}

强类型枚举(enum class)

C++11 新增,避免枚举值冲突,类型安全。

enum class Direction {
    Left,
    Right
};

int main() {
    Direction d = Direction::Left;
    // cout << d << endl; // 错误:强类型枚举不能直接输出
    cout << static_cast<int>(d) << endl; // 输出:0
    return 0;
}

范围 for 循环(C++11)

遍历数组、容器等可迭代对象,简化循环代码。

#include <iostream>
#include <vector>
using namespace std;

int main() {
    int arr[] = {1,2,3,4,5};
    // 遍历数组
    for (int num : arr) {
        cout << num << " ";
    }
    cout << endl;

    vector<string> vec = {"a", "b", "c"};
    // 遍历vector(引用避免拷贝)
    for (auto& s : vec) {
        cout << s << " ";
    }
    cout << endl;
    return 0;
}

extern "C" 机制

C++ 支持调用 C 语言代码,需用extern "C"禁用 C++ 的名称修饰(Name Mangling),确保 C++ 编译器按 C 规则处理函数名。

// C语言头文件(c_func.h)
#ifdef __cplusplus
extern "C" { // 仅在C++编译器中生效
#endif

void c_function(int x); // C语言函数声明

#ifdef __cplusplus
}
#endif

// C语言实现(c_func.c)
#include "c_func.h"
#include <stdio.h>

void c_function(int x) {
    printf("C函数:x=%d\n", x);
}

// C++调用(main.cpp)
#include "c_func.h"
using namespace std;

int main() {
    c_function(10); // 调用C语言函数
    return 0;
}

原理:C++ 为支持函数重载,会修改函数名(如add(int)变为_Z3addi),而 C 语言不修饰函数名。extern "C"让 C++ 编译器按 C 规则生成函数名,保证链接成功。

内存分区

C++ 程序运行时内存分为 5 个区域:

内存分区 存储内容 生命周期 管理方式
栈区 局部变量、函数参数、返回地址 函数结束自动释放 编译器自动分配释放
堆区 new/malloc 分配的对象 手动 delete/free 释放 程序员手动管理
全局 / 静态区 全局变量、static 变量 程序启动分配,结束释放 编译器静态分配
常量区 字符串常量、const 全局常量 程序运行期间 只读,不可修改
代码区 二进制指令、函数体代码 程序运行期间 只读,共享内存

异常处理(Exception)

基本概念

异常是程序运行过程中出现的非正常情况(如除零错误、空指针访问、参数非法等)。C++ 通过 “抛出 - 捕获” 机制处理异常:throw抛出异常,try-catch捕获并处理,避免程序直接崩溃,提升鲁棒性。

异常的抛出与捕获

抛出异常(throw)

使用throw关键字抛出异常,可抛出任意类型(字面量、对象、标准异常类):

/**
 * 除法函数:计算两数相除
 * @param dividend 被除数
 * @param divisor 除数
 * @return float 除法结果
 * @throw const char* 除数为0时抛出异常信息
 */
float divideNumbers(float dividend, float divisor) {
    if (divisor == 0) {
        throw "除数不能为零"; // 抛出字符串类型异常
    }
    return dividend / divisor;
}

捕获异常(try-catch)

  • try块:包裹可能抛出异常的代码;
  • catch块:按类型捕获异常并处理,可设置多个catch匹配不同类型;
  • catch(...):捕获所有未匹配的异常(兜底处理)。
#include <iostream>
using namespace std;

int main() {
    float inputDividend, inputDivisor, calculateResult;
    while (true) {
        cin >> inputDividend >> inputDivisor;
        try {
            calculateResult = divideNumbers(inputDividend, inputDivisor);
            cout << calculateResult << endl;
        } catch (const char* errorMsg) { // 捕获字符串类型异常
            cout << "错误:" << errorMsg << endl;
            break;
        } catch (...) { // 捕获所有其他类型异常
            cout << "未知错误" << endl;
        }
    }
    return 0;
}

异常的类型

可通过不同类型区分异常场景,catch块按类型匹配执行:

#include <iostream>
#include <stdexcept> // 标准异常类头文件
using namespace std;

void checkInput(int inputNum) { // 检查输入值合法性
    if (inputNum == 1) throw 666;                // 整数错误码
    if (inputNum == 2) throw 3.14f;              // 浮点错误码
    if (inputNum == 3) throw runtime_error("参数超出合法范围"); // 标准异常类
}

int main() {
    int userInput;
    cin >> userInput;
    try {
        checkInput(userInput);
    } catch (int intErrorCode) { // 匹配整数类型异常
        cout << "整数错误:" << intErrorCode << endl;
    } catch (float floatErrorCode) { // 匹配浮点类型异常
        cout << "浮点错误:" << floatErrorCode << endl;
    } catch (runtime_error& standardError) { // 匹配标准异常类
        cout << "标准异常:" << standardError.what() << endl; // what()返回异常描述
    }
    return 0;
}

标准异常类

C++ 标准库提供了一系列异常类(继承自exception),常用的有:

  • invalid_argument:参数非法;
  • out_of_range:越界访问;
  • runtime_error:运行时错误;
  • logic_error:逻辑错误(如前置条件不满足)。

示例:使用标准异常类抛出异常

#include <iostream>
#include <stdexcept>
using namespace std;

float divideNumbers(float dividend, float divisor) {
    if (divisor == 0) {
        throw invalid_argument("除数不能为零(标准异常)"); // 抛出标准异常对象
    }
    return dividend / divisor;
}

int main() {
    try {
        divideNumbers(10, 0);
    } catch (invalid_argument& e) {
        cout << e.what() << endl; // 输出:除数不能为零(标准异常)
    }
    return 0;
}

异常与返回错误值的区别

特性 返回错误值 异常处理
传播性 需手动传递错误码(如返回值层层传递) 自动向上传播(直到被捕获)
强制性 易忽略(如未检查返回值) 必须处理(否则程序终止)
信息承载 仅支持基础类型(如 int 错误码) 支持类对象,可携带详细信息
代码可读性 错误处理与业务逻辑混杂 错误处理与业务逻辑分离

析构函数中的异常

析构函数抛出异常可能导致程序崩溃(如栈展开时触发双重异常),因此析构函数内需捕获所有异常:

#include <iostream>
using namespace std;

bool dbConnectionStatus = false; // 数据库连接状态
void closeDbConnection() { // 模拟关闭数据库连接
    if (!dbConnectionStatus) throw 100; // 连接未启用时抛异常
    else cout << "数据库连接正常关闭" << endl;
}

class DbConnector { // 管理数据库连接的类
    bool isClosed = false; // 是否已关闭
public:
    ~DbConnector() { // 析构函数自动关闭连接
        if (!isClosed) {
            try {
                closeDbConnection(); // 尝试关闭
            } catch (...) { // 捕获所有异常,避免崩溃
                cout << "析构时关闭数据库失败" << endl;
            }
        }
    }
    void manualCloseDb() { // 手动关闭(用户可处理异常)
        closeDbConnection();
        isClosed = true;
    }
};

int main() {
    DbConnector dbInstance;
    try {
        dbInstance.manualCloseDb(); // 手动关闭,主动处理异常
    } catch (...) {
        cout << "手动关闭失败,修复连接状态" << endl;
        dbConnectionStatus = true; // 修复状态
    }
    return 0;
}

Lambda 函数(匿名函数)

基本概念

Lambda 函数(匿名函数)是 C++11 引入的函数对象,可直接定义在代码中,无需显式命名,支持捕获外部变量、作为参数传递、作为返回值等特性。常用于简化代码(如一次性函数)、实现函数式编程。

语法结构

[capture list](parameter list) mutable -> return type { function body }
  • capture list(捕获列表):指定捕获外部变量的方式(值捕获 / 引用捕获);
  • parameter list(参数列表):函数参数(同普通函数);
  • mutable:允许修改值捕获的变量(默认值捕获为只读);
  • return type(返回类型):可省略,编译器自动推导;
  • function body(函数体):业务逻辑代码。

捕获列表的用法

值捕获([变量名])

捕获外部变量的副本,Lambda 内部修改不影响外部变量:

#include <iostream>
using namespace std;

int main() {
    int firstNum = 1, secondNum = 2;
    // 值捕获firstNum和secondNum,计算两数之和
    auto sumTwoNumbers = [firstNum, secondNum]() {
        return firstNum + secondNum;
    };
    cout << sumTwoNumbers() << endl; // 输出3
    return 0;
}

引用捕获([& 变量名])

捕获外部变量的引用,Lambda 内部修改会影响外部变量:

int main() {
    int num = 10;
    // 引用捕获num,修改其值
    auto modifyNum = [&num]() {
        num *= 2;
    };
    modifyNum();
    cout << num << endl; // 输出20
    return 0;
}

捕获所有外部变量

  • [=]:值捕获所有外部变量;
  • [&]:引用捕获所有外部变量。
int main() {
    int a = 1, b = 2;
    // 值捕获所有外部变量
    auto printAll = [=]() {
        cout << "a=" << a << ", b=" << b << endl;
    };
    printAll(); // 输出:a=1, b=2

    // 引用捕获所有外部变量
    auto modifyAll = [&]() {
        a += b;
        b *= 2;
    };
    modifyAll();
    cout << "a=" << a << ", b=" << b << endl; // 输出:a=3, b=4
    return 0;
}

Lambda 函数的使用场景

作为函数参数

常用于 STL 算法(如sortcount_if),简化谓词逻辑:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    vector<int> numVector = {3, 1, 4, 1, 5, 9};
    // 统计偶数个数:Lambda作为count_if的谓词参数
    int evenCount = count_if(numVector.begin(), numVector.end(),
        [](int currentNum) { // 参数由count_if自动传递
            return currentNum % 2 == 0;
        });
    cout << "偶数个数:" << evenCount << endl; // 输出2
    return 0;
}

作为函数返回值

Lambda 可作为函数返回值(需用auto推导类型):

/**
 * 创建加法器:返回Lambda函数
 * @param fixedAddend 固定加数
 * @return auto 加法器(接收一个int参数,返回两数之和)
 */
auto createAdder(int fixedAddend) {
    return [fixedAddend](int variableAddend) {
        return fixedAddend + variableAddend;
    };
}

int main() {
    auto addFive = createAdder(5); // 生成“加5”的加法器
    cout << addFive(3) << endl;    // 输出8(5+3)
    cout << addFive(10) << endl;   // 输出15(5+10)
    return 0;
}

高阶函数调用

Lambda 作为参数传递给自定义高阶函数:

/**
 * 检查数字是否满足条件
 * @param targetNum 目标数字
 * @param checkCondition 检查条件(Lambda函数)
 */
void checkNumber(int targetNum, auto checkCondition) {
    if (checkCondition(targetNum)) {
        cout << targetNum << "满足条件" << endl;
    } else {
        cout << targetNum << "不满足条件" << endl;
    }
}

int main() {
    // 检查是否为奇数
    checkNumber(5, [](int num) {
        return num % 2 != 0;
    });
    // 检查是否大于10
    checkNumber(8, [](int num) {
        return num > 10;
    });
    return 0;
}

函数式编程简介

Lambda 是函数式编程的基础,函数式编程强调数据映射而非 “步骤指令”,核心特点:

  • 函数是一等公民(可作为参数、返回值);
  • 避免可变状态,推崇纯函数(输入确定则输出确定);

示例:函数式实现字符串翻转

#include <string>
using namespace std;

// 函数式:通过递归映射实现字符串翻转
string reverseString(const string& inputStr) {
    return inputStr.empty() ? inputStr : 
        reverseString(inputStr.substr(1)) + inputStr[0];
}

int main() {
    string str = "abcdef";
    cout << reverseString(str) << endl; // 输出:fedcba
    return 0;
}

Lambda 的注意事项

  • 捕获列表为空时,需写[](不可省略);
  • 值捕获的变量默认只读,需mutable才能修改;
  • Lambda 的生命周期与普通变量一致,捕获的外部变量若销毁,Lambda 访问会导致未定义行为。
posted @ 2025-12-01 20:45  YouEmbedded  阅读(0)  评论(0)    收藏  举报