C++ 基础与 STL:标准模板库实战
C++ 基础与 STL:标准模板库实战
C++ 是一门兼具性能与抽象的系统级语言,广泛应用于游戏开发、高频交易、嵌入式系统等领域。掌握 C++ 不仅需要理解语法,更要熟练使用标准模板库(STL)。STL 将数据结构和算法分离,通过迭代器粘合,极大提高了开发效率和代码复用性。本教程带你从 C++ 基础出发,逐步深入 STL 核心,最终完成一个实战项目。
第一部分:C++ 快速基础复习
1. 程序结构与基本 I/O
一个最小的 C++ 程序包含 main() 函数,并借助 <iostream> 进行输入输出。
#include <iostream>
using namespace std;
int main() {
cout << "Hello, STL!" << endl;
int age;
cin >> age;
cout << "Your age: " << age << endl;
return 0;
}
cout搭配<<输出,cin搭配>>输入。std::命名空间可避免名称冲突,简单程序可用using namespace std;。
2. 数据类型与变量
C++ 是静态类型语言,常用基本类型:
| 类型 | 关键字 | 示例 |
|---|---|---|
| 整型 | int, short, long, long long |
int x = 42; |
| 浮点型 | float, double, long double |
double pi = 3.14; |
| 字符 | char |
char c = 'A'; |
| 布尔 | bool |
bool flag = true; |
| 字符串 | std::string (需 #include <string>) |
string s = "hello"; |
初始化推荐使用 {} 统一初始化法,防止窄化:int x{10};
3. 控制流
条件分支:
if (score >= 90) {
grade = 'A';
} else if (score >= 80) {
grade = 'B';
} else {
grade = 'C';
}
switch (level) {
case 1: cout << "Beginner"; break;
case 2: cout << "Intermediate"; break;
default: cout << "Advanced";
}
循环:
// for 循环
for (int i = 0; i < 10; ++i) {
cout << i << " ";
}
// while 循环
int j = 0;
while (j < 10) {
cout << j++ << " ";
}
// 范围 for (C++11)
vector<int> v{1,2,3};
for (int val : v) {
cout << val << " ";
}
4. 函数与传递方式
函数定义包含返回类型、函数名和参数列表。参数传递方式影响效率与语义:
- 值传递:拷贝实参,适合小对象。
- 引用传递:操作原对象,避免拷贝,加
const表示只读。 - 指针传递:可空,可用于动态内存。
void print(const string& msg) { cout << msg; } // 常引用
void swap(int& a, int& b) { int t = a; a = b; b = t; } // 引用修改实参
void reset(int* ptr) { *ptr = 0; }
函数重载:相同函数名,参数列表不同(个数或类型)。
5. 数组与指针基础
数组名退化为指针,指针本身是地址变量。
int arr[5] = {1,2,3,4,5};
int* p = arr; // 指向第一个元素
cout << *(p+2); // 输出 3
// 动态内存
int* dynArr = new int[10]; // 堆上分配
delete[] dynArr; // 释放
在现代 C++ 中,建议使用
std::vector和智能指针代替原始指针和 C 数组。
6. 面向对象简要概念
- 类与对象:封装数据与操作。
- 访问控制:
public、private、protected。 - 继承:
class Derived : public Base {};复用代码。 - 多态:通过虚函数实现运行时多态,基类指针或引用调用派生类方法。
面向对象不是本教程重点,STL 本身是基于泛型编程,掌握以上概念即可理解 STL 的设计思想。
第二部分:STL 概述
什么是 STL
STL(Standard Template Library)提供了一组通用模板类和函数,实现了常用数据结构与算法。其核心思想是泛型编程,将数据类型作为参数,使得容器可存放任意类型,算法可作用于不同容器。
STL 六大组件
- 容器:存储数据的对象,如
vector、map。 - 算法:操作数据的函数,如
sort、find。 - 迭代器:类似指针,提供统一访问容器内元素的方法。
- 函数对象(仿函数):重载了
operator()的类或结构体,用于定制算法行为。 - 适配器:修改容器或函数接口,如
stack(适配deque)、reverse_iterator。 - 分配器:控制容器内存分配策略,通常使用默认分配器即可。
前三个组件是实际开发中使用频率最高的部分,也是学习重点。
第三部分:STL 容器详解
所有容器都提供常见的成员函数:size()、empty()、clear()、begin()、end() 等。
1. 序列容器
vector
动态数组,支持随机访问,尾部插入/删除高效。
#include <vector>
vector<int> v;
v.push_back(10);
v.push_back(20);
v.pop_back();
cout << v[0]; // 随机访问
for (auto it = v.begin(); it != v.end(); ++it) { /* ... */ }
for (int x : v) { cout << x << " "; }
常用操作:insert(), erase(), resize(), reserve()(预留空间减少重新分配)。
list
双向链表,任意位置插入删除 O(1),但不支持随机访问。
#include <list>
list<string> names;
names.push_back("Alice");
names.push_front("Bob");
names.sort(); // 链表自带排序
names.remove("Bob");
// 遍历仅支持迭代器或范围 for,不能使用 []
deque
双端队列,两端高效插入删除,支持随机访问,内部由分块内存组成。
#include <deque>
deque<int> dq;
dq.push_back(1);
dq.push_front(2);
int front = dq.front(); // 2
int back = dq.back(); // 1
2. 关联容器
基于红黑树实现,元素自动排序,查找、插入、删除均为 O(log n)。
set 与 multiset
set 不允许重复元素,multiset 允许重复。
#include <set>
set<int> s = {3,1,2,1}; // s 变成 {1,2,3}
s.insert(4);
auto it = s.find(2);
if (it != s.end()) { cout << "found"; }
map 与 multimap
存储键值对,键唯一,值可重复。multimap 允许重复键。
#include <map>
map<string, int> scores;
scores["Alice"] = 95;
scores.insert(make_pair("Bob", 88));
for (auto& p : scores) {
cout << p.first << ": " << p.second << endl;
}
访问不存在的键会默认插入值 0;使用 find() 或 at() 更安全(at() 键不存在时抛出异常)。
3. 无序容器 (C++11)
基于哈希表实现,平均 O(1) 查找,元素无序。
#include <unordered_map>
#include <unordered_set>
unordered_map<string, int> dict;
dict["apple"] = 3;
unordered_set<int> numbers = {10, 20, 30};
4. 容器适配器
适配器封装底层容器,提供特定接口。
stack:后进先出,基于deque,支持push(),pop(),top()。queue:先进先出,基于deque,支持push(),pop(),front(),back()。priority_queue:最大堆(默认),基于vector,top()返回最大元素。
#include <stack>
#include <queue>
priority_queue<int> pq;
pq.push(10); pq.push(5); pq.push(20);
cout << pq.top(); // 20
第四部分:STL 算法与迭代器
迭代器类别
迭代器连接算法与容器,分为五种:
- 输入迭代器:只读,单遍扫描 (
istream_iterator) - 输出迭代器:只写,单遍扫描 (
ostream_iterator) - 前向迭代器:可读写,多遍扫描 (
forward_list迭代器) - 双向迭代器:可前向后移 (
list、set、map迭代器) - 随机访问迭代器:支持 +、-、下标 (
vector、deque、array迭代器)
所有容器提供 begin() 和 end(),反向迭代 rbegin()、rend()。
常用算法示例
需包含 <algorithm> 和 <numeric>。
非修改性算法
find(begin, end, value):返回等于value的第一个位置的迭代器。count(begin, end, value):返回等于value的元素个数。all_of/any_of/none_of:根据条件判断。
vector<int> v = {1,2,3,4,5};
auto it = find(v.begin(), v.end(), 3);
int cnt = count(v.begin(), v.end(), 2);
bool allPos = all_of(v.begin(), v.end(), [](int i){ return i>0; });
修改性算法
copy(src_begin, src_end, dest_begin):拷贝。fill(begin, end, value):填充。transform(begin, end, dest, op):应用函数后输出到目标。replace(begin, end, old_value, new_value)。
排序与搜索
sort(begin, end):默认升序排序,要求随机访问迭代器。stable_sort:稳定排序。binary_search(begin, end, value):二分查找,需有序。lower_bound / upper_bound:返回第一个不小于/大于的位置。
自定义排序:
sort(v.begin(), v.end(), greater<int>()); // 降序
sort(v.begin(), v.end(), [](int a, int b){ return a > b; });
数值算法
accumulate(begin, end, init_value):求和,可指定操作。iota(begin, end, start_value):填充连续递增的值。
第五部分:实战演练 – 学生成绩管理系统
结合所学知识,实现一个控制台程序,功能包括:录入学生成绩、显示平均分、按成绩排序、查找指定学生分数。
1. 数据结构设计
- 使用
std::vector<std::string>存储学生姓名。 - 使用
std::map<std::string, int>存储姓名到成绩的映射。 - 或使用
std::vector<std::pair<std::string, int>>便于排序。
以下选用 vector 存放 pair,既能排序又能保留插入顺序。
2. 完整代码实现
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <numeric>
#include <iomanip>
using namespace std;
struct Student {
string name;
int score;
};
// 按成绩降序比较函数
bool byScoreDesc(const Student& a, const Student& b) {
return a.score > b.score;
}
int main() {
vector<Student> students;
char choice;
do {
cout << "\n===== 学生成绩管理系统 =====\n";
cout << "1. 添加学生成绩\n";
cout << "2. 显示所有学生\n";
cout << "3. 按成绩排序(降序)\n";
cout << "4. 计算平均分\n";
cout << "5. 查找学生分数\n";
cout << "0. 退出\n";
cout << "请选择: ";
cin >> choice;
cin.ignore();
switch (choice) {
case '1': {
string name;
int score;
cout << "姓名: ";
getline(cin, name);
cout << "成绩: ";
cin >> score;
students.push_back({name, score});
cout << "已添加。\n";
break;
}
case '2': {
cout << left << setw(15) << "姓名" << "成绩" << endl;
for (const auto& s : students) {
cout << setw(15) << s.name << s.score << endl;
}
break;
}
case '3': {
sort(students.begin(), students.end(), byScoreDesc);
cout << "已按成绩降序排序。\n";
break;
}
case '4': {
if (students.empty()) {
cout << "暂无学生数据。\n";
break;
}
double avg = accumulate(students.begin(), students.end(), 0.0,
[](double sum, const Student& s) { return sum + s.score; }) / students.size();
cout << "平均分: " << fixed << setprecision(2) << avg << endl;
break;
}
case '5': {
string name;
cout << "输入要查找的姓名: ";
getline(cin, name);
auto it = find_if(students.begin(), students.end(),
[&](const Student& s) { return s.name == name; });
if (it != students.end()) {
cout << name << " 的成绩: " << it->score << endl;
} else {
cout << "未找到该学生。\n";
}
break;
}
case '0':
cout << "再见!\n";
break;
default:
cout << "无效选项。\n";
}
} while (choice != '0');
return 0;
}
3. 代码解读
accumulate利用 lambda 累加Student::score,计算均值。sort配合自定义比较函数byScoreDesc实现降序。find_if配合 lambda 实现按姓名搜索,体现了 STL 算法与容器的协同。- 添加数据时使用
getline处理可能包含空格的姓名,输入流注意cin.ignore()。
通过这个案例,你实践了 vector 容器的动态增长、algorithm 库中的常用算法、迭代器的隐式使用,以及 C++11 的 lambda 表达式和结构化绑定(结构体列表初始化)。
总结与延伸
本教程从 C++ 基础语法切入,带你梳理了 STL 容器、算法和迭代器的核心用法,并通过一个实战项目巩固所学。STL 的能力远不止于此:
- 算法库中还包含堆操作、排列生成、合并、集合运算等。
- 函数对象和lambda让算法定制能力极强。
- 智能指针与STL容器结合形成现代C++内存安全模式。
- 学习并发容器和并行算法(C++17/20)可进一步发挥多核性能。
建议持续通过在线编码平台(如 LeetCode、HackerRank)使用 STL 解决实际问题,逐步内化这种泛型编程思维。掌握 STL,就是掌握 C++ 生产力的核心。