C++ STL入门:vector与字符串流详解
引言
在前面的文章中,我们系统学习了 C++ 异常处理、IO 流体系等核心特性。从本文开始,我们将进入 C++ 标准库中最重要的组成部分——STL(Standard Template Library,标准模板库)的学习。
STL 包含三大核心组件:容器(Containers)、算法(Algorithms)和迭代器(Iterators)。本文将首先介绍字符串流(strstream)的用法作为过渡,然后深入讲解最常用的顺序容器——vector。
第一部分:字符串流 strstream
一、什么是字符串流
字符串流是一种特殊的内存流,它以字符数组作为输入/输出的缓冲区。可以理解为把字符串当作文件来读写。
#include <strstream> // 字符串流头文件(已废弃,但教材中常见) #include <sstream> // 现代 C++ 推荐的字符串流头文件注意:
<strstream>在 C++98 中已被标记为废弃(deprecated),C++17 中彻底移除。现代 C++ 应使用<sstream>中的istringstream、ostringstream、stringstream。但考虑到很多教材和旧代码仍在使用,本文会讲解两种方式。
二、输入字符串流 istrstream
从字符数组中读取数据,就像从文件中读取一样。
#include <iostream> #include <strstream> using namespace std; int main() { // 1. 创建字符流对象,绑定到字符数组 char buf[] = "123567"; istrstream iss(buf); // 2. 获取字符流的大小 iss.seekg(0, ios::end); // 移到末尾 streampos len = iss.tellg(); // 获取位置(即大小) cout << "字符流的大小: " << len << endl; // 3. 回到开头,逐个字符读取 iss.seekg(0, ios::beg); char c; int i = 0; while (i < len) { iss.get(c); cout << c; i++; } cout << endl; return 0; }关键操作:
| 操作 | 说明 |
|---|---|
iss.seekg(0, ios::end) | 移动读指针到末尾 |
iss.tellg() | 获取当前读指针位置 |
iss.seekg(0, ios::beg) | 移动读指针到开头 |
iss.get(c) | 读取一个字符 |
三、输出字符串流 ostrstream
向字符数组中写入数据,就像向文件中写入一样。
#include <iostream> #include <strstream> #include <cstring> using namespace std; int main() { // 1. 创建足够大的缓冲区 char* bufp = new char[1024]{0}; ostrstream oss(bufp, 1024); int opt = 0; while (1) { // 检查缓冲区是否已满 if (oss.tellp() >= 1024) break; cout << "1.录入一行 2.打印所有 3.清空 0.退出\n"; cin >> opt; if (opt == 0) break; else if (opt == 1) { // 录入一行数据 cin.get(); // 清除残留的换行符 char line[32] = ""; cin.getline(line, 32); oss.write(line, strlen(line)); // 写入内容 oss.put('\n'); // 写入换行符 } else if (opt == 2) { // 打印所有行 cout << string(30, '-') << endl; istrstream iss(bufp, (int)oss.tellp()); while (!iss.eof()) { char line[32] = ""; iss.getline(line, 32); cout << line << endl; } } else if (opt == 3) { // 清空缓冲区 oss.seekp(0); // 写指针回到开头 memset(bufp, 0, 1024); // 清零 } } delete[] bufp; return 0; }关键操作:
| 操作 | 说明 |
|---|---|
oss.write(buf, len) | 写入指定长度的数据 |
oss.put(ch) | 写入一个字符 |
oss.tellp() | 获取当前写指针位置(已写入的字节数) |
oss.seekp(0) | 将写指针移回开头 |
四、字符串流的数据流向
五、现代 C++ 的替代方案(sstream)
#include <sstream> #include <string> // 输出字符串流 ostringstream oss; oss << "Hello" << 123 << 3.14; string result = oss.str(); // 获取写入的字符串 // 输入字符串流 istringstream iss("123 456 789"); int a, b, c; iss >> a >> b >> c; // 从字符串中格式化读取第二部分:vector 容器
一、什么是 vector
vector是 C++ STL 中最常用的顺序容器,本质上是一个动态数组。
二、基本操作
#include <iostream> #include <vector> using namespace std; int main() { // 1. 创建 vector vector<int> v1(10); // 10 个元素,默认值为 0 vector<int> v2(10, 5); // 10 个元素,初始值都是 5 // 2. 尾部插入 v1.push_back(10); v1.push_back(20); v1.push_back(30); // 3. 访问元素 cout << v1[0] << endl; // 下标访问(不检查越界) cout << v1.at(1) << endl; // at() 访问(检查越界) cout << v1.front() << endl; // 第一个元素 cout << v1.back() << endl; // 最后一个元素 // 4. 删除尾部元素 v1.pop_back(); // 5. 大小和容量 cout << "size: " << v1.size() << endl; // 元素个数 cout << "capacity: " << v1.capacity() << endl; // 当前容量 cout << "empty: " << v1.empty() << endl; // 是否为空 // 6. 清空 v1.clear(); // 清空所有元素,size=0 return 0; }三、迭代器
迭代器是 STL 的核心概念,可以理解为泛化的指针。
#include <iostream> #include <vector> using namespace std; int main() { vector<int> v1; v1.push_back(10); v1.push_back(20); v1.push_back(30); // 获取迭代器 vector<int>::iterator it1 = v1.begin(); // 指向第一个元素 vector<int>::iterator it2 = v1.end(); // 指向最后一个元素之后 // 使用迭代器遍历 for (vector<int>::iterator it = v1.begin(); it != v1.end(); ++it) { cout << *it << " "; // 解引用获取元素值 } cout << endl; // C++11 范围 for 循环(底层使用迭代器) for (int val : v1) { cout << val << " "; } return 0; }迭代器类型:
四、insert 与 erase
#include <iostream> #include <vector> using namespace std; int main() { vector<int> v1; v1.push_back(10); v1.push_back(20); v1.push_back(30); // 在指定位置插入 vector<int>::iterator it = v1.begin(); it = v1.insert(it, 8); // 在开头插入 8 v1.insert(it + 1, 7); // 在第二个位置插入 7 // 遍历打印 for (int val : v1) { cout << val << " "; // 输出: 8 7 10 20 30 } cout << endl; // 删除指定元素 v1.erase(v1.begin() + 1); // 删除第二个元素(7) // 删除范围 v1.erase(v1.begin(), v1.begin() + 2); // 删除前两个 return 0; }insert 与 erase 的时间复杂度:
| 操作位置 | insert | erase |
|---|---|---|
| 尾部 | O(1) | O(1) |
| 中间/头部 | O(n) | O(n) |
五、从数组创建 vector
#include <iostream> #include <vector> using namespace std; int main() { int arr[] = {1, 2, 3, 4, 5}; // 通过数组创建 vector vector<int> v1(arr, arr + 5); // arr → 数组起始地址 // arr + 5 → 数组结束地址(最后一个元素之后) // 遍历打印 for (vector<int>::iterator it = v1.begin(); it != v1.end(); ++it) { cout << *it << " "; } cout << endl; return 0; }原理:vector的构造函数接受两个迭代器参数,[first, last)左闭右开区间。
第三部分:STL 算法初探
一、for_each 算法
for_each是 STL 中最常用的遍历算法,对区间内的每个元素执行指定操作。
#include <iostream> #include <vector> #include <algorithm> // STL 算法头文件 using namespace std; // 普通函数作为操作 void show(int item) { cout << item << " "; } int main() { vector<int> v1; v1.push_back(1); v1.push_back(2); v1.push_back(3); v1.push_back(4); v1.push_back(5); // 方式1:传入函数指针 for_each(v1.begin(), v1.end(), show); cout << endl; // 方式2:传入 Lambda 表达式(C++11) for_each(v1.begin(), v1.end(), [](int item) { cout << item << " "; } ); cout << endl; return 0; }for_each 的三要素:
| 参数 | 说明 |
|---|---|
v1.begin() | 起始迭代器 |
v1.end() | 结束迭代器 |
show/ lambda | 对每个元素执行的操作 |
二、vector 的构造与拷贝
#include <vector> using namespace std; int main() { vector<int> v1 = {1, 2, 3, 4, 5}; // 多种拷贝构造方式 vector<int> v2 = v1; // 拷贝构造 vector<int> v3(v1); // 拷贝构造 vector<int> v4(v1.begin(), v1.end()); // 迭代器区间构造 vector<int> v5(v1.begin(), v1.begin() + 3); // 部分拷贝 return 0; }第四部分:vector 使用要点
一、常用成员函数速查表
| 函数 | 功能 | 时间复杂度 |
|---|---|---|
push_back(val) | 尾部插入 | O(1) 均摊 |
pop_back() | 尾部删除 | O(1) |
insert(pos, val) | 指定位置插入 | O(n) |
erase(pos) | 删除指定位置 | O(n) |
clear() | 清空所有元素 | O(n) |
size() | 元素个数 | O(1) |
capacity() | 当前容量 | O(1) |
empty() | 判空 | O(1) |
front() | 第一个元素 | O(1) |
back() | 最后一个元素 | O(1) |
at(i) | 带越界检查的访问 | O(1) |
operator[](i) | 不检查越界的访问 | O(1) |
reserve(n) | 预留容量 | O(n) |
shrink_to_fit() | 收缩到合适容量 | O(n) |
二、vector 扩容机制
vector<int> v; v.reserve(100); // 提前预留 100 个元素的空间,避免频繁扩容 for (int i = 0; i < 100; i++) { v.push_back(i); // 这些操作不会触发扩容 }扩容建议:
如果预知元素数量,使用
reserve()提前分配扩容倍数通常是 1.5 或 2,具体取决于编译器实现
三、vector vs 数组
| 对比项 | 原生数组 | vector |
|---|---|---|
| 大小 | 固定 | 动态调整 |
| 越界检查 | 无 | at()有检查 |
| 拷贝 | 需要手动 memcpy | 直接赋值 |
| 内存管理 | 手动 | 自动 |
| 与 STL 算法配合 | 需要包装 | 天然支持 |
总结
一、字符串流要点
| 要点 | 说明 |
|---|---|
istrstream | 从字符数组读取 |
ostrstream | 向字符数组写入 |
seekg/tellg | 读指针定位 |
seekp/tellp | 写指针定位 |
| 现代替代 | stringstream(<sstream>) |
二、vector 核心要点
三、一句话记忆
vector 是会自动扩容的数组,用push_back在尾巴加东西,用迭代器从头到尾遍历,用for_each对每个元素执行操作。
