🚀 C++11 是 C++ 历史上最大的一次"版本更新"——如果说 C++98 是功能手机,那 C++11 就是智能手机。auto、智能指针、Lambda、移动语义……每一个新特性都让代码变得更简洁、更安全、更高效。本文带你逐个击破这些新标准的核心特性!


📖 文章导航

  • 13.1 C++语言标准的发展:C++ 的"编年史"
  • 13.2 C++11关键字及新语法:auto、decltype、nullptr、范围for
  • 13.3 智能指针:unique_ptr、shared_ptr、weak_ptr
  • 13.4 Lambda表达式:匿名函数的艺术
  • 13.5 右值引用与移动语义:性能优化的"黑魔法"
  • 13.6 C++11新增容器:array、forward_list、无序容器、tuple

13.1 C++ 语言标准的发展——C++ 的"编年史"

1.1 C++ 标准发展历程

C++ 标准演进时间线
│
├── 1998  C++98  ── 第一个 ISO 标准,引入 STL
├── 2003  C++03  ── 小修小补,修复了一些缺陷
├── 2011  C++11  ── 🔥 革命性更新!现代 C++ 的起点
├── 2014  C++14  ── C++11 的小幅增强
├── 2017  C++17  ── 结构化绑定、std::optional、文件系统
├── 2020  C++20  ── 🔥 又一次大更新!概念、协程、模块、范围
└── 2023  C++23  ── 最新标准,持续进化中

💡 一句话记忆:C++11 是"智能手机初代",C++14 是"修复补丁",C++17 是"S升级版",C++20 是"全面屏时代"。

1.2 C++11 标准简介——为什么它这么重要?

C++11 带来了几十个新特性,其中最核心的几个:

特性 一句话说明
auto 类型推导 让编译器帮你猜类型
nullptr 终于有了真正的空指针
范围 for 遍历容器不再写 for(int i=0;…)
智能指针 再也不用手动 delete 了
Lambda 表达式 随处定义匿名函数
右值引用 & 移动语义 减少不必要的拷贝,性能飙升
新容器 array、forward_list、unordered_map

1.3 C++20 标准简介——最新力作

特性 一句话说明
概念(Concepts) 给模板参数加上"类型约束"
范围(Ranges) 链式调用的管道式数据处理
协程(Coroutines) 异步编程的新范式
模块(Modules) 告别 #include 的编译速度噩梦

13.2 C++11 关键字及新语法——让代码更简洁

2.1 auto 关键字:让编译器帮你"猜"类型

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

int main() {
    // 以前的写法:手动写类型,又臭又长
    vector<int> v = {1, 2, 3};
    vector<int>::iterator it1 = v.begin();  // 😩 又长又烦

    // 现在的写法:auto 自动推导
    auto it2 = v.begin();  // 😎 编译器自动推导为 vector<int>::iterator
    cout << *it2 << endl;  // 1

    // auto 推导基本类型
    auto a = 42;           // int
    auto b = 3.14;         // double
    auto c = 'A';          // char
    auto d = true;         // bool
    auto e = string("Hi"); // string

    // auto 在复杂类型中特别好用
    map<string, vector<int>> data;
    data["scores"] = {90, 85, 78};
    for (auto& pair : data) {  // 不用写 map<string, vector<int>>::iterator
        cout << pair.first << ": ";
        for (auto& score : pair.second) {
            cout << score << " ";
        }
        cout << endl;
    }

    return 0;
}

⚠️ auto 的注意事项

  • 必须在声明时初始化(编译器需要根据初始化值推导类型)
  • 不要滥用——如果类型不明显,还是显式写出类型更清晰
auto x;           // ❌ 编译错误!auto 必须初始化
auto y = 10;      // ✅
auto z = 3.14;    // ✅

2.2 decltype 关键字:从表达式推导类型

decltype 是 auto 的"好搭档"——它能从一个表达式推导出类型,但不会计算该表达式。

#include <iostream>
using namespace std;

int main() {
    int a = 10;
    double b = 3.14;

    // decltype 推导表达式的类型
    decltype(a + b) c = a + b;  // c 的类型是 double(int + double = double)
    cout << c << endl;           // 13.14

    // decltype 不会计算表达式,只取类型
    decltype(a) d = 100;         // d 的类型是 int
    cout << d << endl;           // 100

    // 实用场景:模板中推导返回值类型
    auto add = [](int x, int y) -> decltype(x + y) {
        return x + y;
    };
    cout << add(3, 4) << endl;  // 7

    // C++14 更简单:直接 auto
    auto add2 = [](auto x, auto y) { return x + y; };
    cout << add2(3, 4.5) << endl;  // 7.5

    return 0;
}

💡 auto vs decltype

  • auto = 根据初始值推导类型
  • decltype = 根据表达式推导类型(不计算表达式)

2.3 nullptr:真正的空指针

// 以前的空指针写法(有坑!)
void func(int)    { cout << "调用了 func(int)" << endl; }
void func(char*)  { cout << "调用了 func(char*)" << endl; }

int main() {
    func(NULL);    // 😱 你期望调用 func(char*),但实际可能调用 func(int)!
                   // 因为 NULL 在某些编译器中被定义为 0

    func(nullptr); // ✅ 明确调用 func(char*)
                   // nullptr 是真正的"空指针",不会被误认为整数 0

    // nullptr 可以隐式转换为任何指针类型
    int* p1 = nullptr;      // ✅
    double* p2 = nullptr;   // ✅
    // int x = nullptr;     // ❌ 编译错误!nullptr 不能转为整数

    return 0;
}

🎯 结论:永远用 nullptr 替代 NULL0,从源头杜绝歧义。

2.4 范围 for 语句:遍历容器的"语法糖"

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

int main() {
    vector<int> nums = {1, 2, 3, 4, 5};

    // 以前的遍历方式
    for (int i = 0; i < nums.size(); i++) {
        cout << nums[i] << " ";
    }
    cout << endl;

    // C++11 范围 for:简洁!
    for (int n : nums) {        // 值拷贝(只读)
        cout << n << " ";
    }
    cout << endl;

    // 引用方式(可以修改)
    for (int& n : nums) {
        n *= 2;  // 每个元素翻倍
    }

    // auto + 引用(最常用!)
    for (auto& n : nums) {
        cout << n << " ";  // 2 4 6 8 10
    }
    cout << endl;

    // 遍历 map
    map<string, int> ages = {{"张三", 20}, {"李四", 21}, {"王五", 19}};
    for (auto& [name, age] : ages) {  // C++17 结构化绑定
        cout << name << ": " << age << endl;
    }

    return 0;
}

💡 最佳实践

  • 只读遍历:for (const auto& x : container)
  • 需要修改:for (auto& x : container)
  • 基本类型可以不用引用:for (int x : nums)

13.3 智能指针——让内存管理自动化

3.1 为什么需要智能指针?

手动管理内存是 C++ 最大的"坑"之一:

// 手动管理内存的各种翻车现场:
void dangerous() {
    int* p = new int(42);

    // 忘了 delete → 内存泄漏 💧
    
    if (someCondition) return;  // 提前返回,泄漏!
    
    delete p;   // 正常释放

    // double free → 程序崩溃 💥
    // delete p;

    // 野指针 → 未定义行为 👻
    // *p = 100;  // p 已经 delete 了,这是野指针!
}

💡 智能指针的核心思想:利用 RAII(资源获取即初始化)——对象销毁时自动释放内存,就像"自动挡"一样,你只管开车,不用担心熄火后忘记关引擎。

3.2 unique_ptr:独占指针(⭐ 最常用!)

unique_ptr 表示独占所有权——同一时刻只有一个 unique_ptr 指向某个对象。

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

int main() {
    // 创建 unique_ptr
    auto p1 = make_unique<int>(42);  // C++14 推荐写法
    cout << *p1 << endl;  // 42

    // 不能拷贝,只能移动
    // auto p2 = p1;         // ❌ 编译错误!独占所有权,不能拷贝
    auto p2 = move(p1);     // ✅ 移动所有权,p1 变为 nullptr

    // 移动后 p1 已经为空
    if (p1 == nullptr) {
        cout << "p1 已经为空了!" << endl;
    }
    cout << *p2 << endl;  // 42

    // 自动释放内存
    {
        auto p3 = make_unique<int>(100);
        cout << *p3 << endl;  // 100
    }   // p3 离开作用域,自动 delete,无需手动释放!

    // 管理数组
    auto arr = make_unique<int[]>(5);
    for (int i = 0; i < 5; i++) {
        arr[i] = i * 10;
    }
    for (int i = 0; i < 5; i++) {
        cout << arr[i] << " ";  // 0 10 20 30 40
    }
    cout << endl;

    return 0;
}

🎯 使用场景:明确只有一个拥有者——比如工厂函数返回新对象、类的独占成员资源。

3.3 shared_ptr:共享指针

shared_ptr 表示共享所有权——多个 shared_ptr 可以指向同一个对象,内部用引用计数管理。

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

int main() {
    // 创建 shared_ptr
    auto p1 = make_shared<int>(42);
    cout << "引用计数: " << p1.use_count() << endl;  // 1

    // 拷贝(引用计数 +1)
    auto p2 = p1;
    cout << "引用计数: " << p1.use_count() << endl;  // 2

    {
        auto p3 = p1;
        cout << "引用计数: " << p1.use_count() << endl;  // 3
    }   // p3 离开作用域,引用计数 -1

    cout << "引用计数: " << p1.use_count() << endl;  // 2

    p2.reset();  // p2 放弃所有权,引用计数 -1
    cout << "引用计数: " << p1.use_count() << endl;  // 1

    // 当最后一个 shared_ptr 销毁时,对象被自动释放
    return 0;
}

引用计数示意图:

shared_ptr p1 ──┐
                │
shared_ptr p2 ──┼──→ [ 对象 42 ] ← 引用计数 = 3
                │
shared_ptr p3 ──┘

当 p3 销毁 → 引用计数 = 2
当 p2 销毁 → 引用计数 = 1
当 p1 销毁 → 引用计数 = 0 → 对象被自动释放!

3.4 weak_ptr:弱引用(解决循环引用)

weak_ptr 是 shared_ptr 的"观察者"——它不增加引用计数,用于打破循环引用。

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

// 循环引用问题:
struct B;  // 前向声明

struct A {
    shared_ptr<B> b_ptr;
    ~A() { cout << "A 被销毁" << endl; }
};

struct B {
    // shared_ptr<A> a_ptr;  // ❌ 如果用 shared_ptr,会造成循环引用,内存泄漏!
    weak_ptr<A> a_ptr;        // ✅ 用 weak_ptr 打破循环
    ~B() { cout << "B 被销毁" << endl; }
};

int main() {
    auto a = make_shared<A>();
    auto b = make_shared<B>();

    a->b_ptr = b;
    b->a_ptr = a;

    cout << "A 引用计数: " << a.use_count() << endl;  // 1(weak_ptr 不增加计数)
    cout << "B 引用计数: " << b.use_count() << endl;  // 1

    // 使用 weak_ptr 前需要 lock()
    if (auto locked = b->a_ptr.lock()) {  // lock() 返回 shared_ptr
        cout << "A 对象还活着!" << endl;
        cout << "A 引用计数: " << locked.use_count() << endl;  // 2
    }

    return 0;
    // a 和 b 销毁时,引用计数归零,A 和 B 都被正确释放
}

输出:

A 引用计数: 1
B 引用计数: 1
A 对象还活着!
A 引用计数: 2
B 被销毁
A 被销毁

📊 三种智能指针对比

特性 unique_ptr shared_ptr weak_ptr
所有权 独占 共享 无(观察者)
能否拷贝 ❌ 只能移动
引用计数 不增加计数
内存开销 最小 较大(控制块) 较小
使用场景 默认首选 多处共享资源 打破循环引用
创建方式 make_unique make_shared 从 shared_ptr 获取

🎯 选择口诀:默认用 unique_ptr;需要共享用 shared_ptr;有循环引用用 weak_ptr


13.4 Lambda 表达式——匿名函数的艺术

Lambda 是 C++11 引入的匿名函数,可以就地定义、就地使用,特别适合短小的回调和算法参数。

4.1 Lambda 基本语法

// 完整语法:
// [捕获列表](参数列表) mutable -> 返回类型 { 函数体 }

// 最简形式:
auto hello = []() { cout << "Hello, Lambda!" << endl; };
hello();  // Hello, Lambda!

// 带参数:
auto add = [](int a, int b) { return a + b; };
cout << add(3, 4) << endl;  // 7

// 带返回类型(多数情况可省略,编译器自动推导):
auto divide = [](double a, double b) -> double {
    if (b == 0) return 0;
    return a / b;
};
cout << divide(10, 3) << endl;  // 3.33333

4.2 捕获列表详解

int main() {
    int x = 10;
    int y = 20;
    string name = "张三";

    // 值捕获(拷贝一份,只读)
    auto f1 = [x, y]() { cout << x + y << endl; };
    f1();  // 30

    // 引用捕获(直接引用原变量,可修改)
    auto f2 = [&x]() { x = 100; };
    f2();
    cout << x << endl;  // 100

    // 隐式值捕获所有(= 表示值捕获所有外部变量)
    auto f3 = [=]() { cout << x << ", " << y << ", " << name << endl; };
    f3();  // 100, 20, 张三

    // 隐式引用捕获所有(& 表示引用捕获所有外部变量)
    auto f4 = [&]() { x = 1; y = 2; name = "李四"; };
    f4();
    cout << x << ", " << y << ", " << name << endl;  // 1, 2, 李四

    // 混合捕获
    auto f5 = [=, &name]() {   // 值捕获所有,但 name 用引用
        name = "王五";          // 可以修改 name
        // x = 1;               // ❌ x 是值捕获,不能修改
        cout << x << ", " << name << endl;
    };
    f5();  // 1, 王五

    return 0;
}

4.3 Lambda 在 STL 算法中的实战

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

int main() {
    vector<int> v = {1, 5, 3, 8, 2, 9, 4, 7, 6};

    // 用 Lambda 自定义排序
    sort(v.begin(), v.end(), [](int a, int b) { return a > b; });
    // 9 8 7 6 5 4 3 2 1

    // 用 Lambda 过滤(打印偶数)
    for_each(v.begin(), v.end(), [](int x) {
        if (x % 2 == 0) cout << x << " ";
    });
    cout << endl;  // 8 6 4 2

    // 用 Lambda 查找
    auto it = find_if(v.begin(), v.end(), [](int x) { return x > 7; });
    if (it != v.end()) cout << "第一个大于7的: " << *it << endl;  // 8

    // 用 Lambda 统计
    int evenCount = count_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; });
    cout << "偶数个数: " << evenCount << endl;  // 4

    // 用 Lambda 变换
    vector<int> squared(v.size());
    transform(v.begin(), v.end(), squared.begin(), [](int x) { return x * x; });
    for (auto& x : squared) cout << x << " ";  // 81 64 49 36 25 16 9 4 1
    cout << endl;

    // 用 Lambda 累加
    int sum = accumulate(v.begin(), v.end(), 0, [](int acc, int x) { return acc + x; });
    cout << "总和: " << sum << endl;  // 45

    return 0;
}

13.5 右值引用与移动语义——性能优化的"黑魔法"

5.1 左值与右值:搞懂"等号两边"

int a = 42;
// a 是左值(有名字、有地址、可以取 &a)
// 42 是右值(临时的、没有持久地址)

int b = a;    // a 是左值,出现在等号右边(读取它的值)
a = b + 1;    // b + 1 是右值(临时计算结果)

// 简单判断法:
// 左值 = 能取地址的、能出现在赋值号左边的
// 右值 = 不能取地址的、临时的(字面量、表达式结果、函数返回值)

5.2 左值引用与右值引用

int a = 42;

// 左值引用:绑定到左值
int& ref1 = a;        // ✅ 正确
// int& ref2 = 42;    // ❌ 编译错误!左值引用不能绑定到右值
const int& ref3 = 42; // ✅ const 左值引用可以绑定到右值(特例)

// 右值引用:绑定到右值(C++11 新增)
int&& rref1 = 42;      // ✅ 正确,绑定到字面量
int&& rref2 = a + 1;   // ✅ 正确,绑定到临时值
// int&& rref3 = a;    // ❌ 编译错误!右值引用不能绑定到左值

// 右值引用可以用 std::move 将左值转为右值
int&& rref4 = std::move(a);  // ✅ move 把 a "转换"为右值

5.3 移动语义:避免不必要的拷贝

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

class BigBuffer {
    int* data;
    size_t size;
public:
    // 构造函数
    BigBuffer(size_t n) : size(n), data(new int[n]) {
        cout << "构造: 分配了 " << n << " 个 int" << endl;
    }

    // 拷贝构造(深拷贝,开销大)
    BigBuffer(const BigBuffer& other) : size(other.size), data(new int[other.size]) {
        copy(other.data, other.data + other.size, data);
        cout << "拷贝构造: 深拷贝了 " << size << " 个 int 😰" << endl;
    }

    // 移动构造(窃取资源,开销极小)
    BigBuffer(BigBuffer&& other) noexcept : data(other.data), size(other.size) {
        other.data = nullptr;  // 把原对象的指针置空,防止重复释放
        other.size = 0;
        cout << "移动构造: 偷走了资源 😎" << endl;
    }

    ~BigBuffer() { delete[] data; }
};

int main() {
    BigBuffer buf1(1000);

    // 拷贝:需要分配新内存 + 复制数据(慢)
    BigBuffer buf2 = buf1;       // 调用拷贝构造

    // 移动:直接窃取指针,不用复制数据(快!)
    BigBuffer buf3 = move(buf1); // 调用移动构造

    return 0;
}

输出:

构造: 分配了 1000 个 int
拷贝构造: 深拷贝了 1000 个 int 😰
移动构造: 偷走了资源 😎

💡 移动语义的核心思想:对于临时对象(右值),与其"复制"它的资源,不如直接"偷"过来——省时省力。

5.4 完美转发:保持参数的值类别

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

void process(int& x)  { cout << "左值: " << x << endl; }
void process(int&& x) { cout << "右值: " << x << endl; }

// 完美转发模板
template<typename T>
void forwarder(T&& arg) {
    process(std::forward<T>(arg));  // 保持 arg 的原始值类别
}

int main() {
    int a = 42;

    forwarder(a);      // a 是左值 → 调用 process(int&)
    forwarder(42);     // 42 是右值 → 调用 process(int&&)

    return 0;
}

输出:

左值: 42
右值: 42

💡 std::forward 的作用:在模板转发中保持参数的"左值/右值属性"不变,避免不必要的拷贝。


13.6 C++11 新增容器——更多选择,更强武器

6.1 array:固定大小数组(安全版原生数组)

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

int main() {
    // 创建 array
    array<int, 5> arr = {5, 2, 8, 1, 9};

    // 和普通数组一样用,但更安全
    cout << "大小: " << arr.size() << endl;     // 5
    cout << "首元素: " << arr.front() << endl;  // 5
    cout << "尾元素: " << arr.back() << endl;   // 9

    // at() 检查越界
    cout << arr.at(2) << endl;   // 8
    // arr.at(10);               // 💥 抛 out_of_range 异常

    // 支持 STL 算法!
    sort(arr.begin(), arr.end());
    for (auto& x : arr) cout << x << " ";  // 1 2 5 8 9
    cout << endl;

    // array 之间可以直接比较
    array<int, 3> a1 = {1, 2, 3};
    array<int, 3> a2 = {1, 2, 3};
    cout << (a1 == a2) << endl;  // 1(true)

    return 0;
}

💡 array vs 原生数组:大小固定时优先用 array——它支持 .size()、越界检查、STL 算法,比原生数组好用太多。

6.2 forward_list:单向链表

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

int main() {
    forward_list<int> fl = {1, 2, 3, 4, 5};

    // 头部操作(O(1))
    fl.push_front(0);
    // 0 1 2 3 4 5

    // 插入(在指定位置之后)
    auto it = fl.begin();
    advance(it, 2);  // 移动到第3个位置
    fl.insert_after(it, 99);
    // 0 1 2 99 3 4 5

    // 删除
    fl.remove(99);
    // 0 1 2 3 4 5

    // 排序
    fl.push_front(9);
    fl.sort();
    // 0 1 2 3 4 5 9

    // 遍历
    for (auto& x : fl) cout << x << " ";
    cout << endl;

    return 0;
}

💡 forward_listlist 更省空间(只有一个指针),适合只需要单向遍历的场景。

6.3 无序容器:哈希表加持的查找

#include <iostream>
#include <unordered_map>
#include <unordered_set>
using namespace std;

int main() {
    // unordered_map:哈希表实现,查找 O(1)(平均)
    unordered_map<string, int> umap;
    umap["apple"] = 3;
    umap["banana"] = 5;
    umap["cherry"] = 2;

    // 查找速度比 map(O(log n))更快!
    if (umap.find("banana") != umap.end()) {
        cout << "找到了 banana: " << umap["banana"] << endl;
    }

    // unordered_set:不重复、无序集合
    unordered_set<int> uset = {3, 1, 4, 1, 5, 9, 2, 6, 5};
    for (auto& x : uset) cout << x << " ";  // 顺序不确定(哈希分布)
    cout << endl;
    cout << "元素数: " << uset.size() << endl;  // 7(去重后)

    return 0;
}

📊 有序容器 vs 无序容器

特性 map / set unordered_map / unordered_set
底层实现 红黑树 哈希表
查找效率 O(log n) O(1) 平均,O(n) 最差
是否有序 ✅ 自动排序 ❌ 无序
内存开销 较小 较大(哈希桶)
适用场景 需要有序遍历 追求极致查找速度

6.4 tuple:万能元组

#include <iostream>
#include <tuple>
#include <string>
using namespace std;

int main() {
    // 创建 tuple(可以存不同类型的数据!)
    tuple<int, string, double> student = {1, "张三", 92.5};

    // 访问元素(用 get<下标>)
    cout << "学号: " << get<0>(student) << endl;   // 1
    cout << "姓名: " << get<1>(student) << endl;   // 张三
    cout << "成绩: " << get<2>(student) << endl;   // 92.5

    // 修改元素
    get<2>(student) = 95.0;

    // 用 tie 解包(C++11)
    int id;
    string name;
    double score;
    tie(id, name, score) = student;
    cout << id << " " << name << " " << score << endl;  // 1 张三 95

    // C++17 结构化绑定(更简洁!)
    auto [sid, sname, sscore] = student;
    cout << sid << " " << sname << " " << sscore << endl;

    // tuple 的大小
    cout << "元素数: " << tuple_size<decltype(student)>::value << endl;  // 3

    // 实用场景:函数返回多个值
    auto getMinMax = [](vector<int>& v) -> tuple<int, int> {
        return {*min_element(v.begin(), v.end()),
                *max_element(v.begin(), v.end())};
    };

    vector<int> nums = {5, 2, 8, 1, 9};
    auto [minVal, maxVal] = getMinMax(nums);
    cout << "最小值: " << minVal << ", 最大值: " << maxVal << endl;  // 1, 9

    return 0;
}

💡 tuple 的使用场景:需要从函数返回多个不同类型值的时候,tuple 比结构体更轻量。


实战综合案例:现代 C++ 版学生成绩管理

把本章学到的所有新特性串起来:

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <memory>
#include <tuple>
#include <unordered_map>
using namespace std;

// 用 tuple 表示一条成绩记录
using ScoreRecord = tuple<string, string, double>;  // 姓名, 科目, 分数

class StudentManager {
    // 用 unordered_map 高效查找
    unordered_map<string, vector<double>> scores;  // 姓名 → 成绩列表

public:
    // 用右值引用接收数据(避免拷贝)
    void addScore(string name, double score) {
        scores[move(name)].push_back(score);
    }

    // 用 Lambda + 算法计算平均分
    double getAverage(const string& name) const {
        auto it = scores.find(name);
        if (it == scores.end() || it->second.empty()) return 0;

        auto& s = it->second;
        return accumulate(s.begin(), s.end(), 0.0) / s.size();
    }

    // 用 tuple 返回排名信息
    tuple<string, double> getTopStudent() const {
        string bestName;
        double bestAvg = 0;

        // 范围 for + 结构化绑定(C++17)
        for (auto& [name, sList] : scores) {
            double avg = accumulate(sList.begin(), sList.end(), 0.0) / sList.size();
            if (avg > bestAvg) {
                bestAvg = avg;
                bestName = name;
            }
        }
        return {bestName, bestAvg};
    }

    // 用智能指针 + Lambda 排序输出
    void printRanking() const {
        // 用 vector + 智能指针管理排名数据
        auto ranking = make_unique<vector<pair<string, double>>>();

        for (auto& [name, sList] : scores) {
            double avg = accumulate(sList.begin(), sList.end(), 0.0) / sList.size();
            ranking->emplace_back(name, avg);
        }

        // Lambda 自定义排序
        sort(ranking->begin(), ranking->end(),
            [](const auto& a, const auto& b) { return a.second > b.second; });

        cout << "=== 成绩排名 ===" << endl;
        int rank = 1;
        for (auto& [name, avg] : *ranking) {
            cout << "第" << rank++ << "名: " << name << " (平均分: " << avg << ")" << endl;
        }
    }
};

int main() {
    auto mgr = make_unique<StudentManager>();

    // 用 move 避免字符串拷贝
    mgr->addScore("张三", 90);
    mgr->addScore("张三", 85);
    mgr->addScore("张三", 78);

    mgr->addScore("李四", 75);
    mgr->addScore("李四", 88);
    mgr->addScore("李四", 91);

    mgr->addScore("王五", 95);
    mgr->addScore("王五", 92);
    mgr->addScore("王五", 88);

    // 打印排名
    mgr->printRanking();

    // 用 auto + tie 获取最优学生
    auto [topName, topAvg] = mgr->getTopStudent();
    cout << "\n最优学生: " << topName << " (平均分: " << topAvg << ")" << endl;

    return 0;
}

输出:

=== 成绩排名 ===
第1名: 王五 (平均分: 91.6667)
第2名: 李四 (平均分: 84.6667)
第3名: 张三 (平均分: 84.3333)

最优学生: 王五 (平均分: 91.6667)

总结:C++11 新标准知识全景图

C++11 新标准
│
├── 🏷️ 新关键字与语法(13.2)
│   ├── auto        → 类型推导,少写类型名
│   ├── decltype    → 从表达式推导类型
│   ├── nullptr     → 替代 NULL,消除歧义
│   └── 范围 for    → for (auto& x : container)
│
├── 🛡️ 智能指针(13.3)
│   ├── unique_ptr  → 独占所有权(默认首选)
│   ├── shared_ptr  → 共享所有权(引用计数)
│   └── weak_ptr    → 弱引用(打破循环)
│
├── 🎭 Lambda 表达式(13.4)
│   → [捕获](参数) { 函数体 }
│   → 配合 STL 算法使用效果拉满
│
├── ⚡ 右值引用与移动语义(13.5)
│   ├── 左值 vs 右值
│   ├── 右值引用(T&&)
│   ├── 移动构造(偷资源而非拷贝)
│   └── 完美转发(forward)
│
└── 📦 新增容器(13.6)
    ├── array          → 安全版固定数组
    ├── forward_list   → 单向链表
    ├── unordered_map  → 哈希表(O(1) 查找)
    └── tuple          → 万能元组

🎯 一句话总结:C++11 让 C++ 从"手动挡"进化成了"自动挡"——auto 帮你推导类型,智能指针帮你管内存,Lambda 让你随处写函数,移动语义帮你省拷贝。学会这些,你的代码就能既简洁又高效!


如果觉得有帮助,欢迎点赞收藏 👍 有问题可以在评论区讨论!

更多推荐