C++ 新标准大冒险:从 C++11 到现代 C++ 的进化之路
🚀 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替代NULL和0,从源头杜绝歧义。
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;
}
💡
arrayvs 原生数组:大小固定时优先用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_list比list更省空间(只有一个指针),适合只需要单向遍历的场景。
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 让你随处写函数,移动语义帮你省拷贝。学会这些,你的代码就能既简洁又高效!
如果觉得有帮助,欢迎点赞收藏 👍 有问题可以在评论区讨论!
更多推荐
所有评论(0)