C++ STL入门:vector与字符串流详解

引言

在前面的文章中,我们系统学习了 C++ 异常处理、IO 流体系等核心特性。从本文开始,我们将进入 C++ 标准库中最重要的组成部分------STL(Standard Template Library,标准模板库)的学习。

STL 包含三大核心组件:容器 (Containers)、算法 (Algorithms)和迭代器 (Iterators)。本文将首先介绍字符串流(strstream)的用法作为过渡,然后深入讲解最常用的顺序容器------vector

第一部分:字符串流 strstream

一、什么是字符串流

字符串流是一种特殊的内存流,它以字符数组作为输入/输出的缓冲区。可以理解为把字符串当作文件来读写

cpp 复制代码
#include <strstream>   // 字符串流头文件(已废弃,但教材中常见)
#include <sstream>     // 现代 C++ 推荐的字符串流头文件

注意<strstream> 在 C++98 中已被标记为废弃(deprecated),C++17 中彻底移除。现代 C++ 应使用 <sstream> 中的 istringstreamostringstreamstringstream。但考虑到很多教材和旧代码仍在使用,本文会讲解两种方式。

二、输入字符串流 istrstream

从字符数组中读取数据,就像从文件中读取一样。

cpp 复制代码
#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

向字符数组中写入数据,就像向文件中写入一样。

cpp 复制代码
#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)

cpp 复制代码
#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 中最常用的顺序容器,本质上是一个动态数组

二、基本操作

cpp 复制代码
#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 的核心概念,可以理解为泛化的指针

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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 中最常用的遍历算法,对区间内的每个元素执行指定操作。

cpp 复制代码
#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 的构造与拷贝

cpp 复制代码
#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 扩容机制

cpp 复制代码
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 对每个元素执行操作。

相关推荐
fqbqrr1 小时前
2605C++,C++类的继承1
c++
Gofarlic_OMS1 小时前
CONVERGE CFD许可不够用?自动回收闲置,燃烧仿真随时跑
java·大数据·开发语言·架构·制造
王老师青少年编程1 小时前
csp信奥赛C++高频考点专项训练之字符串 --【字符串排序】:[NOIP 1998 提高组] 拼数
c++·字符串·csp·高频考点·信奥赛·拼数·字符串排序
重生之我是Java开发战士1 小时前
【Java SE】多线程(二):线程安全、synchronized、volatile与wait/notify详解
java·开发语言·安全
草莓熊Lotso1 小时前
【Linux网络】从 0 到 1 实现高性能 UDP 聊天室:深入拆解 Linux 网络编程与线程池架构
linux·运维·服务器·网络·数据库·c++·udp
暗影八度1 小时前
OpenMetadata Python ingestion 开发环境搭建与运行文档
开发语言·python
basketball6161 小时前
C++ iomanip 常用函数
开发语言·c++
清水白石0081 小时前
从“能装上”到“可复现”:Python 团队如何正确使用 requirements.txt、锁定文件与依赖分组
开发语言·人工智能·python
赏金术士1 小时前
Kotlin 习题集 · 基础篇
android·开发语言·kotlin