C++基础内容主要包括:
C++的数据类型、常量与变量、运算符、命名空间、输入输出、程序运行结构、数组、指针、
内存模型、函数、结构体、引用、类(封装继承多态)、友元和模版。
C++的基础内容相当于是入门知识,配合简单的题目,认真学习4到5天即可学习完成,相当于达到了能手敲图书管理系统的水平。
复习知识CSDN有很多文章,推荐链接,1小时即可看完。
C++进阶学习路线总览
首先需要了解代码编写规范;
接着学习C++的STL(Standard Template Library,标准模板库),可配合数据结构的相关知识
- 线性结构:数组、链表、栈、队列;
- 树形结构:二叉树、红黑树;
- 哈希结构:哈希表、哈希冲突解决(链地址法);
- 算法基础:排序(快速排序、归并排序)、查找(二分查找)。
然后学习现代C++核心特性,重点包括智能指针(RAII思想 )、右值引用与移动语义、Lambda表达式和自动类型推导等C++11/14新特性;
然后学习C++的异常安全与错误处理;
然后学习C++的代码调试方法(GDB)和性能分析(perf/valgrind);
最后学习扩展知识,如设计模式、并发编程、网络编程等。
代码编写规范
1.1 常量命名
// 规范:全大写,下划线分隔,放在常量区或使用 constexpr
const int MAX_BUFFER_SIZE = 1024; // 全局常量
const double PI = 3.14159265358979323846; // 数学常数
constexpr int DEFAULT_TIMEOUT_MS = 5000; // 编译期常量(C++11)
1.2 变量命名
// 规范:小写字母,下划线分隔(snake_case),见名知意
int user_count = 0;
std::string file_path;
1.3 函数命名
// 规范:小写字母,下划线分隔,动词开头
int calculate_sum(int a, int b); // ✅ 动词开头
bool is_valid_user(const std::string& name); // ✅ 布尔值用 is/has/can
std::string get_user_name(); // ✅ 获取值用 get
void set_user_name(const std::string& name); // ✅ 设置值用 set
1.4 类命名
// 规范:大驼峰(PascalCase),名词
class UserProfile; // ✅ 好
class UserManager; // ✅ 好
1.5 成员变量命名
// 规范:小写+下划线,可选后缀下划线表示成员
class GoodExample {
public:
// 公共成员使用正常命名
int public_value;
private:
// 私有成员可选下划线后缀,便于区分
int private_value_; // ✅ 常见风格
std::string user_name_; // ✅ 带下划线后缀
};
2.1 文件头注释
/**
* @file user_manager.h
* @brief 用户管理器类,负责用户的增删改查操作
* @author Your Name
* @date 2024-01-01
*/
2.2 类注释
/**
* @class UserManager
* @brief 管理用户数据的类,提供CRUD操作
*
* @details 这个类负责:
* - 添加新用户到数据库
* - 根据ID查询用户
* - 更新用户信息
* - 删除用户
*/
2.3 函数注释
/**
* @brief 根据用户ID获取用户名
*
* @param user_id 用户ID,必须大于0
* @return std::string 用户名,如果未找到返回空字符串
*
* @throws std::invalid_argument 如果 user_id <= 0
*
* @example
* @code
* UserManager mgr;
* std::string name = mgr.get_user_name(100);
* @endcode
*/
std::string get_user_name(int user_id);
3.1 避免野指针
// 正确做法1:使用智能指针
void demo_smart_pointer() {
// unique_ptr:独占所有权,自动释放
std::unique_ptr<int> smart_ptr = std::make_unique<int>(42);
// 使用指针
std::cout << "Value: " << *smart_ptr << std::endl;
// 函数结束自动释放,无需手动 delete
}
// 正确做法2:使用前检查
void demo_safe_pointer() {
int* ptr = new int(10);
// 使用前检查
if (ptr != nullptr) {
*ptr = 20; // 安全
}
delete ptr; // 记得释放
ptr = nullptr; // 释放后置空,防止悬空指针
}
3.2 避免内存泄漏
3.3 避免数组越界
3.4 避免未初始化变量
3.5 使用 const 保护数据
3.6 使用 enum class 代替 enum
快速参考:命名规范总结
类型 规范 示例
类/结构体 大驼峰 UserManager, BankAccount
函数 小写+下划线 get_user_name(), calculate_sum()
变量 小写+下划线 user_count, file_path
常量 全大写+下划线 MAX_BUFFER_SIZE, PI
成员变量 小写+下划线+_ user_name_, balance_
私有成员 可选下划线后缀 private_value_
命名空间 小写+下划线 my_project::database
枚举值 小写+前缀k Color::kRed, Status::kOk
模板参数 大驼峰或尾缀T TemplateType, T
C++STL
STL 是 C++ 官方提供的通用数据结构和算法库,核心是容器、算法、迭代器,能直接用于工程开发;学习 STL 是提升 C++ 开发效率、对接项目需求的核心步骤,也是面试必考内容。
步骤1:STL容器
序列容器
`std::vector` - 动态数组(最常用)
`std::deque` - 双端队列
`std::list` - 双向链表
`std::array` - 固定大小数组(C++11)
关联容器
`std::pair` - 键值对
`std::map` - 键值对映射(有序)
`std::unordered_map` - 哈希表映射(无序,更快)
`std::set` - 集合(有序,唯一)
`std::unordered_set` - 哈希集合(无序,唯一)
字符串
`std::string` - 字符串类
字符串操作:查找、替换、子串
字符串与数字互转
01:std::vector(重点)
/*
* vector 是什么?
* - 动态数组,大小可以自动增长
* - 元素在内存中连续存储(和数组一样)
* - 支持随机访问,通过下标快速访问任何元素
*
* 什么时候使用 vector?
* - 需要动态大小的数组
* - 需要频繁随机访问元素
* - 不需要在中间频繁插入/删除
*
* vector vs 数组:
* 数组 vector
* --------------------------------------------
* 大小固定 是 否
* 自动管理内存 否 是
* 知道自己大小 否 是 (size())
* 边界检查 否 有 at()
*/
创建vector
// 1. 空vector
vector<int> v1; // 创建空的int vector
cout << "v1 大小: " << v1.size() << endl; // 输出: 0
// 2. 指定大小和初始值
vector<int> v2(5); // 5个元素,默认值为0
cout << "v2: ";
for (int x : v2) cout << x << " "; // 输出: 0 0 0 0 0
cout << endl;
vector<int> v3(5, 10); // 5个元素,每个都是10
cout << "v3: ";
for (int x : v3) cout << x << " "; // 输出: 10 10 10 10 10
cout << endl;
// 3. 用初始化列表
vector<int> v4 = {1, 2, 3, 4, 5};
vector<int> v5{1, 2, 3, 4, 5}; // 等价写法(C++11)
// 4. 用另一个vector初始化
vector<int> v6(v4); // 拷贝v4
vector<int> v7 = v4; // 也是拷贝
// 5. string的vector
vector<string> words = {"hello", "world", "cpp"};
遍历 vector
vector<int> v = {1, 2, 3, 4, 5};
// 方法1: 传统for循环(需要索引时用)
cout << "传统for: ";
for (size_t i = 0; i < v.size(); ++i) {
cout << v[i] << " ";
}
cout << endl;
// 方法2: 范围for循环(C++11,最常用,最安全)
cout << "范围for: ";
for (int x : v) {
cout << x << " ";
}
cout << endl;
// 方法3: 范围for + 引用(需要修改元素时用)
cout << "修改后: ";
for (int& x : v) { // 注意:是 int& 不是 int
x *= 2; // 每个元素乘以2
}
// 方法4: 迭代器
cout << "迭代器: ";
for (auto it = v.begin(); it != v.end(); ++it) {
cout << *it << " ";
}
vector 常用操作速查表
操作 说明
v.size() 元素个数
v.empty() 是否为空
v.capacity() 容量
v.reserve(n) 预分配容量
v.resize(n) 调整大小
v.clear() 清空
v.push_back(x) 末尾添加
v.pop_back() 删除末尾
v[i] 访问(不检查边界)
v.at(i) 访问(检查边界)
v.front() 访问第一个元素
v.back() 访问最后一个元素
v.insert(pos, x) 插入
v.erase(pos) 删除
v.data() 获取原始指针
02:list - 双向链表(重点)
* list 是什么?
* - 双向链表容器
* - 元素在内存中不连续存储
* - 每个元素包含指向前一个和后一个元素的指针
*
* 什么时候使用 list?
* - 需要频繁在中间插入/删除元素
* - 不需要随机访问
* - 迭代器不会失效(除了被删除的那个)
list基础
// 1. 创建和初始化
list<int> lst1; // 空 list
list<int> lst2(5, 10); // 5个10
list<int> lst3 = {1, 2, 3, 4, 5}; // 初始化列表
// 2. 添加元素
lst1.push_back(10); // 末尾添加
lst1.push_front(5); // 开头添加
cout << "lst1: ";
for (int x : lst1) cout << x << " "; // 5 10
cout << endl;
// 3. 访问元素
cout << "front: " << lst1.front() << endl; // 5
cout << "back: " << lst1.back() << endl; // 10
// 注意:list 不支持 [] 和 at()(没有随机访问)
// 4. 大小相关
cout << "size: " << lst1.size() << endl;
cout << "empty: " << (lst1.empty() ? "yes" : "no") << endl;
// 5. 删除元素
lst1.pop_front(); // 删除开头
lst1.pop_back(); // 删除末尾
list插入和删除操作
list<int> lst = {10, 20, 30, 40, 50};
// 1. 在指定位置插入(需要迭代器)
auto it = lst.begin();
advance(it, 2); // 移动到第3个元素
lst.insert(it, 25); // 在索引2处插入25
cout << "插入后: ";
for (int x : lst) cout << x << " "; // 10 20 25 30 40 50
cout << endl;
// 2. 插入多个元素
it = lst.begin();
advance(it, 1);
lst.insert(it, 3, 15); // 插入3个15
cout << "插入多个后: ";
for (int x : lst) cout << x << " ";
cout << endl;
// 3. 删除指定位置
it = lst.begin();
advance(it, 4); // 移动到第5个元素
lst.erase(it); // 删除该元素
cout << "删除后: ";
for (int x : lst) cout << x << " ";
cout << endl;
// 4. 删除范围内的元素
auto first = lst.begin();
auto last = lst.begin();
advance(first, 1);
advance(last, 4);
lst.erase(first, last); // 删除索引1-3的元素
cout << "删除范围后: ";
for (int x : lst) cout << x << " ";
cout << endl;
// 5. 删除特定值的所有元素
lst = {1, 2, 2, 3, 2, 4};
lst.remove(2); // 删除所有值为2的元素
cout << "remove(2)后: ";
for (int x : lst) cout << x << " "; // 1 3 4
cout << endl;
// 6. 删除满足条件的元素
lst = {1, 2, 3, 4, 5, 6};
lst.remove_if([](int x) { return x % 2 == 0; }); // 删除偶数
cout << "删除偶数后: ";
for (int x : lst) cout << x << " "; // 1 3 5
list 的特有操作
list<int> lst1 = {1, 2, 3};
list<int> lst2 = {4, 5, 6};
// 1. splice - 转移元素(不拷贝,只是移动指针)
cout << "splice 前 lst1: ";
for (int x : lst1) cout << x << " ";
cout << endl;
auto it = lst1.begin();
advance(it, 1);
lst1.splice(it, lst2); // 将lst2所有元素插入到lst1的it位置
cout << "splice 后 lst1: ";
for (int x : lst1) cout << x << " "; // 1 4 5 6 2 3
cout << endl;
cout << "splice 后 lst2 大小: " << lst2.size() << endl; // 0(被搬空了)
// 2. merge - 合并两个有序列表
list<int> lst3 = {1, 3, 5, 7};
list<int> lst4 = {2, 4, 6, 8};
lst3.merge(lst4); // lst3和lst4必须都已排序
cout << "\nmerge 后: ";
for (int x : lst3) cout << x << " "; // 1 2 3 4 5 6 7 8
cout << endl;
// 3. sort - 排序(list有自己的sort,比通用算法更高效)
list<int> lst5 = {5, 2, 8, 1, 9};
lst5.sort(); // 升序
cout << "\nsort(升序): ";
for (int x : lst5) cout << x << " ";
cout << endl;
lst5.sort(greater<int>()); // 降序
cout << "sort(降序): ";
for (int x : lst5) cout << x << " ";
cout << endl;
// 4. reverse - 反转
lst5.reverse();
cout << "reverse: ";
for (int x : lst5) cout << x << " ";
cout << endl;
// 5. unique - 删除连续重复元素
list<int> lst6 = {1, 1, 2, 2, 2, 3, 3};
lst6.unique();
cout << "\nunique: ";
for (int x : lst6) cout << x << " "; // 1 2 3
03:pair - 键值对基础
// 1. 创建 pair
pair<string, int> p1("Alice", 25);
cout << "p1: " << p1.first << ", " << p1.second << endl;
// 2. 使用 make_pair(类型推导)
auto p2 = make_pair("Bob", 30);
cout << "p2: " << p2.first << ", " << p2.second << endl;
// 3. 初始化列表(C++11)
pair<string, int> p3{"Charlie", 35};
04:map-有序键值对映射
// 1. 创建和初始化
map<string, int> scores;
scores = {
{"Alice", 90},
{"Bob", 85},
{"Charlie", 95}
};
// 2. 访问元素
cout << "Alice 的分数: " << scores["Alice"] << endl;
cout << "Bob 的分数: " << scores.at("Bob") << endl;
// 3. 添加/修改元素
scores["David"] = 88; // 添加
scores["Alice"] = 92; // 修改
05:unordered_map - 哈希表映射(重点)
unordered_map 使用哈希表,平均 O(1) 查找
// 1. 查找
auto it = ages.find("Bob");
if (it != ages.end()) {
cout << "找到: " << it->first << " -> " << it->second << endl;
}
// 2. 遍历(注意:无序!)
for (const auto& [name, age] : ages) {
cout << " " << name << ": " << age << endl;
}
// 3. 哈希表特性
cout << "\nunordered_map 特性:" << endl;
cout << " 桶数量: " << ages.bucket_count() << endl;
cout << " 最大桶数: " << ages.max_bucket_count() << endl;
cout << " 负载因子: " << ages.load_factor() << endl;
06:set - 有序集合
set 存储唯一值,自动排序(可去重)
set<int> s;
s.insert(5);
s.insert(2);
s.insert(8);
s.insert(2); // 重复值被忽略
for (int x : s) {
cout << x << " "; // 2 5 8
}
07:array
* array 是什么?
* - 固定大小数组的STL封装
* - 大小在编译时确定
* - 不分配动态内存,开销为零
*
* 什么时候使用 array?
* - 知道数组大小,不会改变
* - 需要STL接口(如begin/end, size)
* - 比原生数组更安全
// 1. 创建和初始化array<int, 5> arr1; // 未初始化
array<int, 5> arr2{}; // 零初始化
array<int, 5> arr3{1, 2, 3, 4, 5}; // 列表初始化
array<int, 5> arr4 = {10, 20, 30}; // 剩余为0
// 2. 访问元素
cout << "arr3[0] = " << arr3[0] << endl;
cout << "arr3.at(1) = " << arr3.at(1) << endl;
cout << "front: " << arr3.front() << endl;
cout << "back: " << arr3.back() << endl;
// 3. 大小
cout << "size: " << arr3.size() << endl; // 编译期常量
// 4. 遍历
cout << "遍历: ";
for (int x : arr3) {
cout << x << " ";
}
array<int, 10> arr = {5, 2, 8, 1, 9, 3, 7, 4, 6, 0};// 1. 排序
sort(arr.begin(), arr.end());
cout << "排序后: ";
for (int x : arr) cout << x << " ";
cout << endl;
// 2. 查找
auto it = find(arr.begin(), arr.end(), 5);
if (it != arr.end()) {
cout << "找到 5,位置: " << distance(arr.begin(), it) << endl;
}
// 3. 二分查找
bool found = binary_search(arr.begin(), arr.end(), 7);
cout << "7 存在: " << (found ? "是" : "否") << endl;
// 4. 统计
int count = std::count(arr.begin(), arr.end(), 5);
cout << "5 的个数: " << count << endl;
// 5. 遍历修改
for_each(arr.begin(), arr.end(), [](int& x) { x *= 2; });
cout << "翻倍后: ";
for (int x : arr) cout << x << " ";
cout << endl;
// 6. 填充
arr.fill(0);
cout << "fill(0)后: ";
for (int x : arr) cout << x << " ";
cout << endl;
08:string
// 1. 创建和初始化
string s1; // 空字符串
string s2 = "Hello"; // 从字符串字面量
string s3("World"); // 构造函数
string s4(5, 'A'); // 5个'A': "AAAAA"
string s5(s2); // 拷贝构造
string s6 = s2 + " " + s3; // 拼接: "Hello World"
// 1. 比较运算符cout << "apple == banana: " << (s1 == s2 ? "true" : "false") << endl;
cout << "apple < banana: " << (s1 < s2 ? "true" : "false") << endl;
cout << "apple == apple: " << (s1 == s3 ? "true" : "false") << endl;
// 2. compare() 函数(类似strcmp)
int result = s1.compare(s2);
if (result < 0) {
cout << "apple < banana (compare返回负数)" << endl;
} else if (result > 0) {
cout << "apple > banana (compare返回正数)" << endl;
} else {
cout << "apple == banana (compare返回0)" << endl;
}
// 3. 部分比较
result = s1.compare(1, 3, s3); // 比较s1[1:3]和s3
cout << "\"ppl\" 与 \"apple\" 比较: " << result << endl;
// 1. to_string() - 数字转字符串int i = 42;
double d = 3.14159;
string s_i = to_string(i);
string s_d = to_string(d);
cout << "int to string: " << s_i << endl;
cout << "double to string: " << s_d << endl;
// 2. stoi() - 字符串转整数
string num_str = "123";
int num = stoi(num_str);
cout << "string to int: " << num << endl;
// 3. stod() - 字符串转浮点数
string pi_str = "3.14159";
double pi = stod(pi_str);
cout << "string to double: " << pi << endl;
09迭代器
* 什么是迭代器?
* - 迭代器是STL连接容器和算法的"桥梁"
* - 提供统一的方式访问容器中的元素
* - 类似指针,但更安全、更通用
*
* 为什么需要迭代器?
* - 使算法与容器解耦
* - 同一个算法可以用于不同容器
* - 提供安全的访问方式
vector<int> v = {1, 2, 3, 4, 5};// begin() / end() - 返回可修改的迭代器
cout << "使用 begin/end: ";
for (auto it = v.begin(); it != v.end(); ++it) {
*it *= 2; // 可以修改元素
cout << *it << " ";
}
cout << endl;
// cbegin() / cend() - 返回 const 迭代器(只读)
cout << "使用 cbegin/cend: ";
for (auto it = v.cbegin(); it != v.cend(); ++it) {
// *it *= 2; // ❌ 编译错误!不能修改
cout << *it << " ";
}
cout << endl;
// rbegin() / rend() - 反向迭代器
cout << "使用 rbegin/rend(反向): ";
for (auto it = v.rbegin(); it != v.rend(); ++it) {
cout << *it << " ";
}
cout << endl;
// crbegin() / crend() - const 反向迭代器
cout << "使用 crbegin/crend(const反向): ";
for (auto it = v.crbegin(); it != v.crend(); ++it) {
cout << *it << " ";
}
vector<int> v = {10, 20, 30, 40, 50};// 1. 解引用:*it 获取元素
auto it = v.begin();
cout << "*it = " << *it << endl; // 输出: 10
// 2. 箭头操作:it-> 访问成员(元素是类对象时)
vector<string> words = {"hello", "world"};
auto word_it = words.begin();
cout << "word_it->size() = " << word_it->size() << endl; // 输出: 5
// 3. 前置/后置递增
++it; // 前置:返回递增后的值(更高效)
it++; // 后置:返回递增前的值
cout << "++it 后: " << *it << endl; // 输出: 20
// 4. 前置/后置递减(双向迭代器以上)
--it;
cout << "--it 后: " << *it << endl; // 输出: 10
// 5. 随机访问(仅随机访问迭代器)
it = v.begin();
it += 2; // 向前移动2步
cout << "it += 2: " << *it << endl; // 输出: 30
it = v.begin() + 3; // 直接跳到第4个元素
cout << "begin() + 3: " << *it << endl; // 输出: 40
// 6. 迭代器相减(仅随机访问迭代器)
auto first = v.begin();
auto last = v.end();
cout << "距离: " << (last - first) << endl; // 输出: 5
// 7. 比较迭代器
if (v.begin() != v.end()) {
cout << "容器不为空" << endl;
}
10:适配器和仿函数
适配器不是「全新的组件」,而是对容器、迭代器、仿函数进行「包装」,改变其原有接口,适配新的使用场景 。因此适配器分为容器适配器(stack(栈)、queue(队列)、priority_queue(优先队列))、迭代器适配器reverse_iterator(反向迭代器)和仿函数适配器。
适配器的核心是容器适配器
stack(栈):基于deque(默认)/vector/list包装,只暴露「尾部插入 / 删除 / 访问」接口(LIFO,后进先出);queue(队列):基于deque(默认)包装,只暴露「尾部插入、头部删除、访问」接口(FIFO,先进先出);priority_queue(优先队列):基于vector(默认)+ 堆算法实现,自动按优先级排序(默认大顶堆)。
仿函数 = 可以像函数一样调用的对象,本质是重载了 operator() 的类
它伪装成函数,但实际上是对象
// 定义仿函数:判断整数是否为偶数
struct IsEven {
// 重载()运算符,参数是要判断的整数,返回布尔值
bool operator()(int num) const {
// const保证仿函数不修改自身状态,符合STL算法要求
return num % 2 == 0; }
};
int main()
{ vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8};
// 使用仿函数:传入IsEven的临时对象作为判断规则
int even_count = count_if(nums.begin(), nums.end(), IsEven());
cout << "偶数的个数:" << even_count << endl; // 输出:4
return 0; }
template <class T>struct greater {
bool operator()(const T& a, const T& b) const {
return a > b; }
};
11:STL总结--基于学生成绩管理系统
代码结构
StudentSystem/
├── student.h # 学生类定义
├── student.cpp # 学生类实现
├── grade_manager.h # 成绩管理器类定义
├── grade_manager.cpp # 成绩管理器类实现
├── main.cpp # 主程序入口
└── README.md
代码比较简单,直接编译即可:
g++ -std=c++17 main.cpp student.cpp grade_manager.cpp -o student_system
按照代码编写顺序来看
cpp
//==============================================================
student.h - 学生类定义
//==============================================================
#ifndef STUDENT_H
#define STUDENT_H
#include <string>
#include <vector>
class Student {
public:
// 构造函数
Student();
Student(int id, const std::string& name, int age);
// 获取器
int get_id() const;
std::string get_name() const;
int get_age() const;
const std::vector<int>& get_grades() const;
// 修改器
void set_name(const std::string& name);
void set_age(int age);
// 成绩管理
void add_grade(int grade);
double get_average() const;
int get_max_grade() const;
int get_min_grade() const;
size_t get_grade_count() const;
// 显示信息
void display() const;
// 比较(用于排序)
bool operator<(const Student& other) const;
private:
int id_;
std::string name_;
int age_;
std::vector<int> grades_;
};
#endif // STUDENT_H
cpp
//==============================================================
student.cpp - 学生类实现
//==============================================================
#include "student.h"
#include <iostream>
#include <algorithm>
#include <numeric>
#include <iterator>
using namespace std;
// 构造函数
Student::Student() : id_(0), name_(""), age_(0) {}
Student::Student(int id, const string& name, int age)
: id_(id), name_(name), age_(age) {}
// 获取器
int Student::get_id() const {
return id_;
}
string Student::get_name() const {
return name_;
}
int Student::get_age() const {
return age_;
}
const vector<int>& Student::get_grades() const {
return grades_;
}
// 修改器
void Student::set_name(const string& name) {
name_ = name;
}
void Student::set_age(int age) {
age_ = age;
}
// 成绩管理
void Student::add_grade(int grade) {
grades_.push_back(grade);
}
double Student::get_average() const {
if (grades_.empty()) {
return 0.0;
}
// 使用 accumulate 算法求和
int sum = accumulate(grades_.begin(), grades_.end(), 0);
return static_cast<double>(sum) / grades_.size();
}
int Student::get_max_grade() const {
if (grades_.empty()) {
return 0;
}
// 使用 max_element 算法
auto it = max_element(grades_.begin(), grades_.end());
return *it;
}
int Student::get_min_grade() const {
if (grades_.empty()) {
return 0;
}
// 使用 min_element 算法
auto it = min_element(grades_.begin(), grades_.end());
return *it;
}
size_t Student::get_grade_count() const {
return grades_.size();
}
// 显示信息
void Student::display() const {
cout << "ID: " << id_
<< ", 姓名: " << name_
<< ", 年龄: " << age_
<< ", 平均分: " << get_average()
<< endl;
}
// 比较(用于排序)
bool Student::operator<(const Student& other) const {
return id_ < other.id_;
}
cpp
//==============================================================
grade_manager.h - 成绩管理器类定义
//==============================================================
#ifndef GRADE_MANAGER_H
#define GRADE_MANAGER_H
#include "student.h"
#include <unordered_map>
#include <vector>
#include <functional>
class GradeManager {
public:
// 构造函数
GradeManager() = default;
// ===== 学生管理 =====
// 添加学生
bool add_student(int id, const std::string& name, int age);
// 删除学生
bool remove_student(int id);
// 查找学生
Student* find_student(int id);
const Student* find_student(int id) const;
// 检查学生是否存在
bool has_student(int id) const;
// 获取所有学生
std::vector<Student> get_all_students() const;
// ===== 成绩管理 =====
// 为学生添加成绩
bool add_grade(int student_id, int grade);
// ===== 统计功能 =====
// 获取班级平均分
double get_class_average() const;
// 获取最高分学生
std::vector<Student> get_top_students(int top_n = 1) const;
// 按平均分排序的学生列表
std::vector<Student> get_students_sorted_by_average() const;
// 按条件筛选学生(使用仿函数/Lambda)
std::vector<Student> filter_students(std::function<bool(const Student&)> predicate) const;
// 统计各分数段人数
struct GradeStats {
int excellent; // 90-100
int good; // 80-89
int pass; // 60-79
int fail; // 0-59
};
GradeStats get_grade_statistics() const;
// ===== 显示功能 =====
// 显示所有学生
void display_all() const;
// 显示统计信息
void display_statistics() const;
// 获取学生数量
size_t get_student_count() const;
private:
// 使用 unordered_map 存储学生(ID -> Student)
std::unordered_map<int, Student> students_;
};
#endif // GRADE_MANAGER_H
cpp
//==============================================================
grade_manager.cpp - 成绩管理器类实现
//==============================================================
#include "grade_manager.h"
#include <iostream>
#include <algorithm>
#include <numeric>
#include <sstream>
using namespace std;
// ===== 学生管理 =====
bool GradeManager::add_student(int id, const string& name, int age) {
// 检查ID是否已存在
if (students_.count(id) > 0) {
cout << "错误: 学生ID " << id << " 已存在" << endl;
return false;
}
// 使用 emplace 插入(更高效)
students_.emplace(id, Student(id, name, age));
cout << "添加学生: " << name << " (ID: " << id << ")" << endl;
return true;
}
bool GradeManager::remove_student(int id) {
// 使用 erase 删除
size_t erased = students_.erase(id);
if (erased > 0) {
cout << "删除学生ID: " << id << endl;
return true;
}
cout << "错误: 学生ID " << id << " 不存在" << endl;
return false;
}
Student* GradeManager::find_student(int id) {
auto it = students_.find(id);
if (it != students_.end()) {
return &(it->second);
}
return nullptr;
}
const Student* GradeManager::find_student(int id) const {
auto it = students_.find(id);
if (it != students_.end()) {
return &(it->second);
}
return nullptr;
}
bool GradeManager::has_student(int id) const {
return students_.count(id) > 0;
}
vector<Student> GradeManager::get_all_students() const {
vector<Student> result;
result.reserve(students_.size());
// 使用 transform 算法提取学生
transform(students_.begin(), students_.end(),
back_inserter(result),
[](const auto& pair) {
return pair.second;
});
return result;
}
// ===== 成绩管理 =====
bool GradeManager::add_grade(int student_id, int grade) {
Student* student = find_student(student_id);
if (student == nullptr) {
cout << "错误: 学生ID " << student_id << " 不存在" << endl;
return false;
}
student->add_grade(grade);
cout << "为学生ID " << student_id << " 添加成绩: " << grade << endl;
return true;
}
// ===== 统计功能 =====
double GradeManager::get_class_average() const {
if (students_.empty()) {
return 0.0;
}
double total = 0.0;
int count = 0;
// 使用范围for遍历
for (const auto& [id, student] : students_) {
double avg = student.get_average();
if (avg > 0) {
total += avg;
count++;
}
}
return count > 0 ? total / count : 0.0;
}
vector<Student> GradeManager::get_top_students(int top_n) const {
auto all_students = get_students_sorted_by_average();
// 截取前 top_n 个
if (static_cast<size_t>(top_n) >= all_students.size()) {
return all_students;
}
return vector<Student>(all_students.begin(),
all_students.begin() + top_n);
}
vector<Student> GradeManager::get_students_sorted_by_average() const {
auto all = get_all_students();
// 使用 sort 算法 + Lambda 排序
sort(all.begin(), all.end(),
[](const Student& a, const Student& b) {
return a.get_average() > b.get_average(); // 降序
});
return all;
}
vector<Student> GradeManager::filter_students(function<bool(const Student&)> predicate) const {
vector<Student> result;
auto all = get_all_students();
// 使用 copy_if 算法筛选
copy_if(all.begin(), all.end(),
back_inserter(result),
predicate);
return result;
}
GradeManager::GradeStats GradeManager::get_grade_statistics() const {
GradeStats stats = {0, 0, 0, 0};
// 遍历所有学生的所有成绩
for (const auto& [id, student] : students_) {
for (int grade : student.get_grades()) {
if (grade >= 90) {
stats.excellent++;
} else if (grade >= 80) {
stats.good++;
} else if (grade >= 60) {
stats.pass++;
} else {
stats.fail++;
}
}
}
return stats;
}
// ===== 显示功能 =====
void GradeManager::display_all() const {
cout << "\n========== 所有学生 ==========" << endl;
auto all = get_all_students();
// 使用 for_each 算法显示
for_each(all.begin(), all.end(),
[](const Student& s) {
s.display();
});
cout << "总人数: " << all.size() << endl;
}
void GradeManager::display_statistics() const {
cout << "\n========== 统计信息 ==========" << endl;
cout << "学生总数: " << get_student_count() << endl;
cout << "班级平均分: " << get_class_average() << endl;
GradeStats stats = get_grade_statistics();
cout << "成绩分布:" << endl;
cout << " 优秀(90-100): " << stats.excellent << endl;
cout << " 良好(80-89): " << stats.good << endl;
cout << " 及格(60-79): " << stats.pass << endl;
cout << " 不及格(0-59): " << stats.fail << endl;
// 显示前3名
auto top = get_top_students(3);
cout << "\n成绩前三名:" << endl;
for (size_t i = 0; i < top.size(); ++i) {
cout << " 第" << (i + 1) << "名: "
<< top[i].get_name()
<< " (平均分: " << top[i].get_average() << ")" << endl;
}
}
size_t GradeManager::get_student_count() const {
return students_.size();
}
现代C++核心特性
现代 C++ 特性解决传统 C++ 的内存安全问题, 提升代码运行效率, 降低代码冗余度
A智能指针
* 智能指针是RAII(资源获取即初始化)的核心应用,
* 自动管理动态内存的生命周期,防止内存泄漏。
#include <memory> // 智能指针头文件
#include <utility>
unique_ptr - 独占所有权
// 创建方式1: make_unique (C++14推荐)
unique_ptr<Widget> w1 = make_unique<Widget>(1, "Unique Widget 1");
w1->greet();
// 创建方式2: new (不推荐,但需要知道)
unique_ptr<Widget> w2(new Widget(2, "Unique Widget 2"));
cout << "移动所有权:\n";
unique_ptr<Widget> w3 = move(w1); // w1变为空
// reset() 释放所有权
cout << "\nreset释放:\n";
w3.reset(); // 显式释放,Widget被析构
// release() 释放所有权但不删除对象
auto raw = w2.release(); // w2变为空,返回裸指针
cout << "w2是否为空: " << (w2 ? "否" : "是") << "\n";
delete raw; // 手动删除
shared_ptr - 共享所有权
// 创建方式1: make_shared (推荐,效率更高)
shared_ptr<Widget> s1 = make_shared<Widget>(100, "Shared Widget 1");
cout << "引用计数: " << s1.use_count() << "\n";
{
// 复制增加引用计数
shared_ptr<Widget> s2 = s1;
cout << "复制后引用计数: " << s1.use_count() << "\n";
s2->greet();
} // s2离开作用域,引用计数减1
cout << "s2离开作用域后引用计数: " << s1.use_count() << "\n";
// 移动不增加引用计数,转移所有权
shared_ptr<Widget> s3 = move(s1);
cout << "移动后s1引用计数: " << s1.use_count() << "\n";
cout << "移动后s3引用计数: " << s3.use_count() << "\n";
weak_ptr - 解决循环引用
node1->next = node2;
node2->next = node1; // weak_ptr不增加引用计数
B右值引用与移动语义
* 移动语义是C++11最重要的特性之一,通过"移动"而非"复制"
* 来避免不必要的资源拷贝,大幅提升性能。
int a = 10; // a是左值(有名字,有内存地址)
int& b = a; // b是左值引用
// int&& c = a; // 错误!不能将左值绑定到右值引用
int&& d = 10; // 10是右值(临时对象,无名字),d是右值引用
int&& e = a * 2; // a*2是右值表达式
cout << "\n判断方法:\n";
cout << "能取地址(&)的是左值,不能取地址的是右值\n";
void take_lvalue_ref(int& x) {cout << " 接受左值引用: " << x << "\n";
}
void take_rvalue_ref(int&& x) {
cout << " 接受右值引用: " << x << "\n";
}
string str1 = "Hello";
cout << " str1: " << str1 << "\n";
// std::move将左值强制转换为右值引用
string str3 = move(str1); // str1的资源被"移动"到str3
std::move 的本质是「强制类型转换工具」,不是「移动数据的函数」:
std::move(x)只做一件事:把「左值 x」强制转换成「右值引用类型」,让编译器选择调用「移动构造 / 赋值函数」,而非「拷贝构造 / 赋值函数」;- 真正执行「数据 / 资源转移」的是移动构造函数 (
string类实现的),不是std::move; - str1 变空,是
string的移动构造函数「拿走了 str1 的资源」,和std::move本身无关。
string str3 = move(str1);
分两步执行,核心在第二步:
第一步:move(str1) ------ 仅做类型转换:把 str1 的类型从 std::string 转换成 std::string&&(右值引用);
第二步:string str3 = 右值引用 ------ 移动构造函数执行 "资源转移"
// string的移动构造函数(接收右值引用)
string(string&& other) noexcept { // 1. 拿走other的资源(指针指向"Hello"的内存)
this->data_ = other.data_;
this->size_ = other.size_;
// 2. 把other的资源置空(避免析构时重复释放)
other.data_ = nullptr;
other.size_ = 0;
}
std::forward 与完美转发
// 万能引用模板函数
template<typename T>
void relay(T&& arg) { // T&& 是万能引用,不是右值引用
cout << " relay收到: ";
if (std::is_lvalue_reference<T>::value) {
cout << "左值\n";
} else {
cout << "右值\n";
}
// 使用forward保持值类别
process(std::forward<T>(arg));
}
// 完美转发示例
template<typename T>
void wrapper(T&& arg) {
cout << " wrapper -> ";
// forward保持参数的左值/右值性质
process(std::forward<T>(arg));
}
CLambda表达式
* Lambda是C++11最重要的特性之一,提供匿名函数的便捷写法。
Lambda完整语法:
[捕获列表](参数列表) mutable(可选) exception(可选) -> 返回类型 { 函数体 };
Lambda 表达式的本质是 C++ 编译器自动生成的「匿名仿函数类(Functor)」的实例对象 ------ 它不是 "函数",而是一个「行为像函数的对象(可调用对象)」
// 1. 值捕获(拷贝)
auto capture_by_value = [x]() {
cout << " 值捕获 x = " << x << "\n";
// x++; // 错误!值捕获默认是const的
};
capture_by_value();
// 2. 引用捕获
auto capture_by_ref = [&y]() {
cout << " 引用捕获 y = " << y << "\n";
y++; // 可以修改
};
capture_by_ref();
cout << " 修改后 y = " << y << "\n";
// 3. 混合捕获
auto mixed_capture = [x, &y]() {
cout << " 混合捕获: x = " << x << ", y = " << y << "\n";
};
mixed_capture();
不使用mutable:值捕获的变量是const的
泛型Lambda (C++14)
// C++14: auto参数
auto print = [](auto x) {
cout << " 值: " << x << ", 类型: ";
if constexpr (is_same_v<decltype(x), int>)
cout << "int\n";
else if constexpr (is_same_v<decltype(x), double>)
cout << "double\n";
else if constexpr (is_same_v<decltype(x), string>)
cout << "string\n";
else
cout << "其他\n";
};
Lambda与STL算法
vector<int> nums = {5, 2, 8, 1, 9, 3, 7, 4, 6};
// for_each
cout << "\nfor_each打印平方:\n ";
for_each(nums.begin(), nums.end(), [](int n) {
cout << n * n << " ";
});
// accumulate with Lambda
int sum = 0;
for_each(nums.begin(), nums.end(), [&sum](int n) {
sum += n;
});
给Lambda 表达式起名字
// 定义Lambda表达式,命名为divide(赋值给auto变量)
auto divide = [](int a, int b) -> double {
// 把a转为double,避免整数除法(10/3=3),得到浮点数结果(3.333...)
return static_cast<double>(a) / b; };
// 调用Lambda,输出结果 cout << " divide(10, 3) = " << divide(10, 3) << "\n";
// 输出3.33333...
捕获this
class Counter {
private:
int count_; // 私有成员变量
public:
void demo() {
count_ = 10;
// Lambda想访问count_,但是count_是类的成员
// Lambda必须通过this指针来访问成员
auto lambda = [this]() {
cout << this->count_ << endl;
};
lambda(); // 调用这个Lambda表达式
}
};