C++ 进阶知识
本章将深入探讨 C++ 的高级特性,包括模板编程、STL 标准库、异常处理、智能指针、Lambda 表达式和多线程编程。掌握这些内容将使你能够编写更通用、更高效、更健壮的 C++ 代码。
模板编程
通俗类比
想象你去奶茶店点饮料。你可以点"大杯珍珠奶茶"、"中杯抹茶拿铁"——模板就像这个菜单模板,你不用为每一种杯型和口味单独写一份制作流程,只需要写一份"模板流程",然后填入不同的配料和杯型即可。
模板(Template)是 C++ 最强大的特性之一,它允许你编写与类型无关的通用代码。通过模板,你可以创建能够处理任意数据类型的函数和类,实现代码复用的最大化。
函数模板
函数模板让你编写一个通用的函数,可以处理多种数据类型:
#include <iostream> using namespace std; // 函数模板定义 template <typename T> T maxVal(T a, T b) { return (a > b) ? a : b; } int main() { cout << maxVal(3, 5) << endl; // T 被推导为 int cout << maxVal(3.5, 2.1) << endl; // T 被推导为 double cout << maxVal('a', 'z') << endl; // T 被推导为 char return 0; }
类模板
类模板让你创建通用的数据结构:
#include <iostream> using namespace std; // 通用的盒子类 template <typename T> class Box { private: T value; public: Box(T v) : value(v) {} void set(T v) { value = v; } T get() { return value; } }; int main() { Box<int> intBox(100); // 整数盒子 Box<double> doubleBox(3.14); // 浮点数盒子 Box<string> strBox("Hello"); // 字符串盒子 cout << intBox.get() << endl; // 100 cout << doubleBox.get() << endl; // 3.14 cout << strBox.get() << endl; // Hello return 0; }
多个模板参数
// 键值对类,两个类型参数 template <typename K, typename V> class Pair { public: K key; V value; Pair(K k, V v) : key(k), value(v) {} }; // 使用 Pair<string, int> p1("age", 20); Pair<int, double> p2(1, 98.5);
模板在编译时实例化,不会产生运行时开销。现代 C++ 中,typename 和 class 在模板参数中可以互换使用。
编程练习
编写模板函数 maxVal(T a, T b) 返回两个值中较大的一个。在 main 中分别用 int、double、char 三种类型测试该函数。
STL 容器
通俗类比
STL 就像一套乐高积木。vector 是可伸缩的收纳盒,list 是手拉手的小朋友队伍,map 是查字典——你不用从零制造这些容器,直接拿来用,把精力放在"搭什么建筑"(解决什么问题)上。
STL(Standard Template Library,标准模板库)是 C++ 标准库的核心组成部分,提供了一系列通用的数据结构和算法。STL 容器是用于存储和管理数据的类模板。
序列容器
| 容器 | 头文件 | 特点 | 适用场景 |
|---|---|---|---|
vector | <vector> | 动态数组,随机访问快 | 频繁随机访问,尾部增删 |
deque | <deque> | 双端队列,两端操作快 | 头部尾部都需要增删 |
list | <list> | 双向链表,插入删除快 | 频繁中间插入删除 |
forward_list | <forward_list> | 单向链表,内存占用小 | 只需要单向遍历 |
vector(动态数组)
vector 是最常用的 STL 容器,它会自动管理内存,可以根据需要动态增长:
#include <iostream> #include <vector> using namespace std; int main() { // 创建 vector vector<int> nums; // 空 vector vector<int> nums2(5); // 5 个元素,初始化为 0 vector<int> nums3 = {1, 2, 3, 4, 5}; // 列表初始化 // 添加元素 nums.push_back(10); // 在尾部添加 nums.emplace_back(20); // C++11,更高效 // 访问元素 cout << nums3[0] << endl; // 1(不检查越界) cout << nums3.at(0) << endl; // 1(检查越界,安全) cout << nums3.front() << endl; // 第一个元素 cout << nums3.back() << endl; // 最后一个元素 // 获取大小 cout << nums3.size() << endl; // 元素个数 cout << nums3.empty() << endl; // 是否为空 // 删除元素 nums3.pop_back(); // 删除最后一个 nums3.clear(); // 清空所有 // 遍历(C++11 范围 for) vector<int> v = {10, 20, 30}; for (int x : v) { cout << x << " "; } return 0; }
关联容器
| 容器 | 头文件 | 特点 | 适用场景 |
|---|---|---|---|
set | <set> | 有序集合,元素唯一 | 需要去重和排序 |
map | <map> | 有序键值对,按键排序 | 需要按键查找 |
unordered_set | <unordered_set> | 哈希集合,查找 O(1) | 快速查找,不关心顺序 |
unordered_map | <unordered_map> | 哈希映射,查找 O(1) | 最快的键值查找 |
map(映射)
#include <iostream> #include <map> #include <string> using namespace std; int main() { // 创建 map:键是 string,值是 int map<string, int> scores; // 插入元素 scores["张三"] = 90; scores["李四"] = 85 scores.insert({"王五", 95}); // 访问元素 cout << scores["张三"] << endl; // 90 // 检查键是否存在 if (scores.count("赵六") == 0) { cout << "赵六不存在" << endl; } // 遍历(按键的升序) for (const auto& pair : scores) { cout << pair.first << ": " << pair.second << endl; } return 0; }
auto 关键字(C++11)让编译器自动推导变量类型,简化代码。unordered_map 使用哈希表实现,平均查找时间复杂度为 O(1),比 map 的 O(log n) 更快。
编程练习
创建一个 vector<int>,存入用户输入的 5 个整数,然后使用 STL 算法完成以下操作并输出结果:
- 使用
sort排序 - 使用
reverse反转 - 使用
accumulate求和 - 使用
count统计某个值出现的次数
迭代器
通俗类比
迭代器就像一本书的书签。你可以把书签放在某一页(指向某个元素),翻页(++ 向后移动),回看(-- 向前移动),直接跳到某一章(随机访问)。没有书签,你就很难快速找到特定内容。
迭代器(Iterator)是 STL 中用于遍历容器元素的对象,它提供了一种统一的方式来访问不同容器中的数据,类似于指针。
迭代器的基本使用
#include <iostream> #include <vector> using namespace std; int main() { vector<int> v = {10, 20, 30, 40, 50}; // 使用迭代器遍历 vector<int>::iterator it; for (it = v.begin(); it != v.end(); ++it) { cout << *it << " "; // 解引用获取值 } // 使用 auto 简化(C++11) for (auto it = v.begin(); it != v.end(); ++it) { cout << *it << " "; } // const_iterator:不允许修改 for (auto it = v.cbegin(); it != v.cend(); ++it) { // *it = 100; // 错误!不能修改 cout << *it << " "; } return 0; }
迭代器类型
| 迭代器类型 | 能力 | 支持的容器 |
|---|---|---|
| 输入迭代器 | 只读,单次遍历 | istream |
| 输出迭代器 | 只写,单次遍历 | ostream |
| 前向迭代器 | 可读写,多遍遍历 | forward_list, unordered_* |
| 双向迭代器 | 可前进后退 | list, set, map |
| 随机访问迭代器 | 可随机跳转 | vector, deque, array |
编程练习
定义一个 vector<string> 存储 {"apple", "banana", "cherry", "date"}。使用迭代器遍历容器,输出每个字符串及其长度,不使用范围 for 循环。
STL 算法
通俗类比
STL 算法就像厨房里的多功能料理机。sort 是自动排序机,find 是雷达搜索器,copy 是复印机。这些工具已经由顶级厨师(标准库开发者)调试好了,你直接选用,比自己从头造一台机器要可靠得多。
STL 提供了大量通用的算法函数,定义在 <algorithm> 头文件中。这些算法可以与各种容器配合使用。
常用算法
#include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6}; // 排序 sort(v.begin(), v.end()); // 升序排序 sort(v.begin(), v.end(), greater<int>()); // 降序排序 // 查找 auto it = find(v.begin(), v.end(), 5); if (it != v.end()) cout << "找到了: " << *it << endl; // 二分查找(需要先排序) bool exists = binary_search(v.begin(), v.end(), 5); // 统计 int count = count(v.begin(), v.end(), 1); // 最值 auto minIt = min_element(v.begin(), v.end()); auto maxIt = max_element(v.begin(), v.end()); // 反转 reverse(v.begin(), v.end()); // 去重(需要先排序) sort(v.begin(), v.end()); auto last = unique(v.begin(), v.end()); v.erase(last, v.end()); return 0; }
编程练习
定义一个 vector<int> 包含 {45, 12, 78, 23, 67, 89, 34}。使用 STL 算法完成:
- 排序并输出
- 查找值为 67 的元素位置
- 统计大于 50 的元素个数(使用
count_if) - 将所有元素乘以 2(使用
transform)
异常处理
通俗类比
异常处理就像火灾报警系统。try 是"正常工作区",catch 是"消防员"——当某个地方出问题时(火灾),报警器会响(throw),消防员立即赶到处理(catch),而不会让整个大楼(程序)崩溃。
异常处理是一种处理程序运行时错误的机制。它允许程序在发生错误时"抛出"一个异常,然后在合适的地方"捕获"并处理它。
基本语法
#include <iostream> #include <stdexcept> using namespace std; // 可能抛出异常的函数 double divide(double a, double b) { if (b == 0) { throw runtime_error("除数不能为零!"); // 抛出异常 } return a / b; } int main() { try { // 可能抛出异常的代码 double result = divide(10, 0); cout << "结果: " << result << endl; } catch (const exception& e) { // 捕获并处理异常 cout << "错误: " << e.what() << endl; } cout << "程序继续执行..." << endl; return 0; }
多个 catch 块
try { // 可能抛出多种异常的代码 } catch (const invalid_argument& e) { cout << "参数错误: " << e.what() << endl; } catch (const runtime_error& e) { cout << "运行时错误: " << e.what() << endl; } catch (...) { // 捕获所有其他异常 cout << "未知错误" << endl; }
异常处理不应该用于控制正常程序流程,只应用于真正的异常情况。过度使用异常会影响性能。C++ 提倡"异常安全"的编程方式。
编程练习
编写函数 double safeSqrt(double x),当 x 为负数时抛出 invalid_argument 异常。在 main 中捕获异常并输出友好提示信息。
智能指针
通俗类比
普通指针就像借出去的东西没有登记——容易忘记归还(内存泄漏)。智能指针就像图书馆的自动借还系统,当你不再使用这本书(离开作用域)时,系统会自动归还(释放内存),再也不怕"借书不还"了。
智能指针是 C++11 引入的自动内存管理工具,它们封装了原始指针,在对象不再使用时自动释放内存,有效防止内存泄漏。
三种智能指针
| 智能指针 | 头文件 | 特点 |
|---|---|---|
unique_ptr | <memory> | 独占所有权,不能复制 |
shared_ptr | <memory> | 共享所有权,引用计数 |
weak_ptr | <memory> | 弱引用,不增加引用计数 |
unique_ptr
#include <iostream> #include <memory> using namespace std; int main() { // 创建 unique_ptr unique_ptr<int> ptr(new int(10)); // 更推荐的创建方式(C++14) auto ptr2 = make_unique<int>(20); // 使用 cout << *ptr << endl; // 10 cout << *ptr2 << endl; // 20 // 获取原始指针 int* raw = ptr.get(); // 释放所有权 int* released = ptr.release(); delete released; // 需要手动释放 // 重置(释放旧对象,指向新对象) ptr2.reset(new int(30)); // 不能复制 unique_ptr! // auto ptr3 = ptr2; // 编译错误 // 可以移动所有权 auto ptr3 = move(ptr2); return 0; // ptr 和 ptr3 自动释放内存 }
shared_ptr
#include <iostream> #include <memory> using namespace std; int main() { // 创建 shared_ptr auto sp1 = make_shared<int>(100); { auto sp2 = sp1; // 复制,引用计数 +1 cout << sp1.use_count() << endl; // 2 auto sp3 = sp1; // 引用计数 +1 cout << sp1.use_count() << endl; // 3 } // sp2 和 sp3 销毁,引用计数 -2 cout << sp1.use_count() << endl; // 1 // sp1 销毁时,引用计数变为 0,内存自动释放 return 0; }
避免循环引用问题:如果两个 shared_ptr 互相引用,引用计数永远不会变为 0,导致内存泄漏。此时应使用 weak_ptr 打破循环。
编程练习
定义一个 Resource 类,构造函数输出"获取资源",析构函数输出"释放资源"。在 main 中使用 unique_ptr 和 shared_ptr 各管理一个 Resource 对象,观察构造和析构的输出顺序。
Lambda 表达式
通俗类比
Lambda 就像一次性便签纸。有时候你只需要写一句简单的指令(比如"把价格打八折"),不需要专门印一本手册(定义函数)。Lambda 让你快速写一条"用完即扔"的简短代码。
Lambda 表达式(C++11)是创建匿名函数对象的简洁方式。它让你可以在需要函数的地方直接定义函数,而不需要单独写一个命名函数。
基本语法
// 语法:[捕获列表](参数列表) -> 返回类型 { 函数体 } #include <iostream> using namespace std; int main() { // 最简单的 lambda auto sayHello = []() { cout << "Hello!" << endl; }; sayHello(); // 带参数的 lambda auto add = [](int a, int b) -> int { return a + b; }; cout << add(3, 5) << endl; // 8 // 返回类型可自动推导 auto multiply = [](int a, int b) { return a * b; }; return 0; }
捕获列表
捕获列表决定了 lambda 可以访问哪些外部变量:
int x = 10, y = 20; // [] - 不捕获任何外部变量 // [=] - 以值方式捕获所有变量 // [&] - 以引用方式捕获所有变量 // [x] - 只以值捕获 x // [&x] - 只以引用捕获 x // [x, &y] - x 值捕获,y 引用捕获 auto lambda1 = [=]() { return x + y; }; // 值捕获,不能修改 auto lambda2 = [&]() { x++; y++; }; // 引用捕获,可以修改 auto lambda3 = [x, &y]() { y = x + 10; }; // 混合捕获
Lambda 与 STL 算法结合
#include <vector> #include <algorithm> #include <iostream> using namespace std; int main() { vector<int> v = {1, 2, 3, 4, 5}; // 使用 lambda 作为回调函数 for_each(v.begin(), v.end(), [](int n) { cout << n * n << " "; // 输出平方 }); // 使用 lambda 作为排序条件 vector<string> names = {"Bob", "Alice", "Charlie"}; sort(names.begin(), names.end(), [](const string& a, const string& b) { return a.length() < b.length(); // 按长度排序 }); // 使用 lambda 查找 auto it = find_if(v.begin(), v.end(), [](int n) { return n > 3; // 第一个大于 3 的元素 }); return 0; }
Lambda 表达式本质上是一个匿名函数对象(functor),编译器会自动生成一个类。它们非常适合作为 STL 算法的回调函数,代码更加简洁直观。
编程练习
编写程序:定义 vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},使用 Lambda 表达式完成:
- 输出所有偶数(for_each + lambda)
- 统计大于 5 的数的个数(count_if + lambda)
- 将所有元素平方后存入新 vector(transform + lambda)
命名空间
通俗类比
命名空间就像不同班级的学生名册。一班有个"张伟",二班也有个"张伟"——命名空间(一班、二班)让同名的学生不会混淆。using namespace 就像"我只看一班的名册",不用每次都写"一班.张伟"。
命名空间(namespace)用于组织代码,避免命名冲突。在大型项目或使用多个第三方库时尤其重要。
基本用法
#include <iostream> // 定义命名空间 namespace Math { const double PI = 3.14159; double square(double x) { return x * x; } double circleArea(double r) { return PI * r * r; } } namespace Utils { void print(const std::string& msg) { std::cout << "[Utils] " << msg << std::endl; } } int main() { // 使用 :: 运算符访问 std::cout << Math::PI << std::endl; std::cout << Math::circleArea(5.0) << std::endl; Utils::print("Hello"); // using 声明 using Math::PI; std::cout << PI << std::endl; // 可以直接使用 PI // using namespace(谨慎使用) using namespace Utils; print("World"); return 0; }
嵌套命名空间(C++17)
// C++17 简洁语法 namespace Game::Graphics::Rendering { class Shader { /* ... */ }; } // 等价于 namespace Game { namespace Graphics { namespace Rendering { class Shader { /* ... */ }; } } }
匿名命名空间
// 匿名命名空间中的内容只在当前文件可见 namespace { int internalCounter = 0; // 只在当前文件可见 void helperFunction() { /* ... */ } } // 等价于 C 语言中的 static
编程练习
创建两个命名空间 Math 和 Physics,各自定义一个 PI 常量(值不同)和一个 calculate() 函数。在 main 中分别调用两个命名空间的函数并输出结果。
预处理指令
通俗类比
预处理器就像剧本的准备工作。在正式开拍前,导演先把"#张三 替换为 主演"(宏替换),把"所有场景A的剧本粘贴进来"(文件包含)。这些都是在"开拍前"(编译前)完成的准备工作。
预处理器在编译之前处理源代码,以 # 开头的指令都是预处理指令。
常用预处理指令
// 包含头文件 #include <iostream> // 系统头文件 #include "myheader.h" // 用户头文件 // 宏定义 #define PI 3.14159 #define SQUARE(x) ((x) * (x)) // 条件编译 #ifdef DEBUG cout << "调试模式" << endl; #else cout << "发布模式" << endl; #endif // 防止头文件重复包含 #ifndef MY_HEADER_H #define MY_HEADER_H // 头文件内容 #endif // 更现代的方式(pragma once) #pragma once // 放在头文件最开头
预定义宏
| 宏 | 说明 |
|---|---|
__LINE__ | 当前行号 |
__FILE__ | 当前文件名 |
__DATE__ | 编译日期 |
__TIME__ | 编译时间 |
__func__ | 当前函数名(C++11) |
#include <iostream> using namespace std; #define LOG(msg) cout << __FILE__ << ":" << __LINE__ \ << " [" << __func__ << "] " << msg << endl void test() { LOG("函数被调用"); // 输出: main.cpp:10 [test] 函数被调用 } int main() { cout << "编译日期: " << __DATE__ << endl; cout << "编译时间: " << __TIME__ << endl; test(); return 0; }
编程练习
使用宏定义和条件编译编写程序:定义宏 DEBUG 时输出调试信息,未定义时只输出常规信息。使用 #ifdef 控制不同编译行为。
多线程编程
通俗类比
单线程就像一个人 sequentially 做家务:先洗碗、再拖地、再晾衣服。多线程就像一家人分工合作:爸爸洗碗、妈妈拖地、孩子晾衣服——三个人同时干活,整体速度大大提升!
多线程允许程序同时执行多个任务。C++11 引入了标准的线程库 <thread>,让跨平台的多线程编程成为可能。
创建线程
#include <iostream> #include <thread> using namespace std; // 线程函数 void worker(int id) { cout << "线程 " << id << " 正在运行" << endl; } int main() { // 创建线程 thread t1(worker, 1); thread t2(worker, 2); // 使用 lambda 创建线程 thread t3([]() { cout << "Lambda 线程" << endl; }); // 等待线程完成 t1.join(); t2.join(); t3.join(); cout << "所有线程已完成" << endl; return 0; }
线程同步(互斥锁)
当多个线程访问共享数据时,需要使用互斥锁(mutex)来防止数据竞争:
#include <iostream> #include <thread> #include <mutex> using namespace std; mutex mtx; // 互斥锁 int counter = 0; // 共享数据 void increment() { for (int i = 0; i < 100000; i++) { mtx.lock(); // 加锁 counter++; // 临界区 mtx.unlock(); // 解锁 } } // 更安全的写法:lock_guard void incrementSafe() { for (int i = 0; i < 100000; i++) { lock_guard<mutex> lock(mtx); // RAII 自动管理锁 counter++; } // 离开作用域自动解锁 }
其他同步工具
| 工具 | 头文件 | 用途 |
|---|---|---|
mutex | <mutex> | 基本的互斥锁 |
lock_guard | <mutex> | RAII 方式自动管理锁 |
unique_lock | <mutex> | 更灵活的锁管理 |
condition_variable | <condition_variable> | 条件变量,线程间通信 |
atomic | <atomic> | 原子操作,无锁编程 |
future/promise | <future> | 异步任务和结果获取 |
多线程编程的核心挑战是数据竞争和死锁。尽量使用高级同步原语(如 lock_guard、atomic),避免手动管理锁。遵循"以数据为中心"的设计,减少共享状态。
进阶知识学习完成!
接下来可以学习 C++ 图形界面,使用 Qt 框架开发桌面应用程序。
编程练习
创建两个线程同时向一个 vector<int> 中添加元素(各添加 1000 个)。分别测试以下两种情况并观察结果差异:
- 不加互斥锁
- 使用
mutex加锁保护
课后练习与项目实践
基础练习
作业 1:模板交换函数
编写一个模板函数 swapValues(T& a, T& b),可以交换任意两个同类型变量的值。在主函数中测试 int、double、char 三种类型。
参考实现
#include <iostream> using namespace std; template <typename T> void swapValues(T& a, T& b) { T temp = a; a = b; b = temp; } int main() { int x = 3, y = 7; swapValues(x, y); cout << "int: " << x << " " << y << endl; double a = 1.5, b = 2.5; swapValues(a, b); cout << "double: " << a << " " << b << endl; char c1 = 'A', c2 = 'Z'; swapValues(c1, c2); cout << "char: " << c1 << " " << c2 << endl; return 0; }
作业 2:vector 综合练习
创建一个 vector<int>,存入10个随机数(1-100),然后:
1. 输出所有元素
2. 用 sort 从小到大排序并输出
3. 删除所有偶数(使用 erase-remove 惯用法)
4. 输出剩余元素
参考实现
#include <iostream> #include <vector> #include <algorithm> #include <cstdlib> #include <ctime> using namespace std; int main() { srand(time(0)); vector<int> nums; for (int i = 0; i < 10; i++) nums.push_back(rand() % 100 + 1); cout << "原始: "; for (int n : nums) cout << n << " "; cout << endl; sort(nums.begin(), nums.end()); cout << "排序: "; for (int n : nums) cout << n << " "; cout << endl; nums.erase(remove_if(nums.begin(), nums.end(), [](int x){ return x % 2 == 0; }), nums.end()); cout << "去偶: "; for (int n : nums) cout << n << " "; cout << endl; return 0; }
作业 3:异常安全除法
编写除法函数 safeDivide(double a, double b),当 b 为 0 时抛出异常。在主函数中用 try-catch 捕获并提示"不能除以零"。
参考实现
#include <iostream> #include <stdexcept> using namespace std; double safeDivide(double a, double b) { if (b == 0) throw runtime_error("不能除以零!"); return a / b; } int main() { double a, b; cout << "输入两个数: "; cin >> a >> b; try { cout << "结果: " << safeDivide(a, b) << endl; } catch (const exception& e) { cout << "错误: " << e.what() << endl; } return 0; }
进阶练习
作业 4:智能指针管理对象数组
定义一个 Book 类(书名、价格),使用 vector<shared_ptr<Book>> 管理一个图书馆的书目。实现添加书籍、显示所有书籍、计算总价功能。
参考实现
#include <iostream> #include <vector> #include <memory> #include <string> using namespace std; class Book { public: string title; double price; Book(string t, double p) : title(t), price(p) {} }; int main() { vector<shared_ptr<Book>> library; library.push_back(make_shared<Book>("C++ Primer", 89.0)); library.push_back(make_shared<Book>("Effective C++", 65.0)); library.push_back(make_shared<Book>("STL源码剖析", 79.0)); double total = 0; for (auto& b : library) { cout << b->title << " ¥" << b->price << endl; total += b->price; } cout << "总价: ¥" << total << endl; return 0; }
作业 5:多线程计数器
创建两个线程,一个线程将全局变量 count 从 0 加到 50000,另一个线程也从 0 加到 50000(共享同一个 count)。观察结果为什么不是 100000,并尝试用 mutex 解决。
参考实现
#include <iostream> #include <thread> #include <mutex> using namespace std; int count = 0; mutex mtx; void increment() { for (int i = 0; i < 50000; i++) { lock_guard<mutex> lock(mtx); // 自动加锁解锁 count++; } } int main() { thread t1(increment); thread t2(increment); t1.join(); t2.join(); cout << "最终结果: " << count << endl; // 正确输出 100000 return 0; }
综合项目:单词频率统计器
项目目标
编写程序读取一段英文文本(从文件或直接写在程序里),统计每个单词出现的次数,按频率从高到低输出前10个单词。要求使用 map 存储,使用 sort 配合 Lambda 排序。
参考实现
#include <iostream> #include <map> #include <vector> #include <algorithm> #include <sstream> #include <string> using namespace std; int main() { string text = "the quick brown fox jumps over the lazy dog the dog was very lazy"; map<string, int> freq; stringstream ss(text); string word; while (ss >> word) freq[word]++; vector<pair<string, int>> items(freq.begin(), freq.end()); sort(items.begin(), items.end(), [](auto& a, auto& b) { return a.second > b.second; }); cout << "Top 10 单词:" << endl; for (int i = 0; i < min(10, (int)items.size()); i++) { cout << items[i].first << ": " << items[i].second << endl; } return 0; }