C++ 基础与 STL:标准模板库实战

FreeGuideOnline 最新 2026-06-12

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. 面向对象简要概念

  • 类与对象:封装数据与操作。
  • 访问控制publicprivateprotected
  • 继承class Derived : public Base {}; 复用代码。
  • 多态:通过虚函数实现运行时多态,基类指针或引用调用派生类方法。

面向对象不是本教程重点,STL 本身是基于泛型编程,掌握以上概念即可理解 STL 的设计思想。


第二部分:STL 概述

什么是 STL

STL(Standard Template Library)提供了一组通用模板类和函数,实现了常用数据结构与算法。其核心思想是泛型编程,将数据类型作为参数,使得容器可存放任意类型,算法可作用于不同容器。

STL 六大组件

  1. 容器:存储数据的对象,如 vectormap
  2. 算法:操作数据的函数,如 sortfind
  3. 迭代器:类似指针,提供统一访问容器内元素的方法。
  4. 函数对象(仿函数):重载了 operator() 的类或结构体,用于定制算法行为。
  5. 适配器:修改容器或函数接口,如 stack(适配 deque)、reverse_iterator
  6. 分配器:控制容器内存分配策略,通常使用默认分配器即可。

前三个组件是实际开发中使用频率最高的部分,也是学习重点。


第三部分: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:最大堆(默认),基于 vectortop() 返回最大元素。
#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 迭代器)
  • 双向迭代器:可前向后移 (listsetmap 迭代器)
  • 随机访问迭代器:支持 +、-、下标 (vectordequearray 迭代器)

所有容器提供 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++ 生产力的核心。