Featured image of post C++STL库入门

C++STL库入门

C++ STL

C++STL库入门

C++常用结构(语法)

取消C与C++的标准输入输出流绑定

在程序开头运行下面的代码即可。

1
2
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);

这会取消C与C++的标准输入输出流绑定,代码中输入和输出的部分要不就用C的,要不就用C++的,不然会导致输入输出流混乱。

lambda 表达式

lambda 表达式(也叫匿名函数)是 C++11 及后续标准引入的特性,简单来说,它允许你在代码中就地定义一个临时的、短小的函数,无需像普通函数那样先声明 / 定义再调用。

lambda 表达式的声明如下:

1
2
3
[捕获变量] (参数列表) 可变修饰 异常说明 -> 返回类型 {
    // 函数体(执行逻辑)
}
部分 作用 是否可选
[捕获变量] 决定lambda能访问哪些外部变量,以及访问方式(值/引用) 必选
(参数列表) 和普通函数的参数列表完全一致 可选(无参数可省略)
可变修饰 值捕获的变量默认是const,加mutable可修改副本(不影响原变量) 可选,一般不用
异常说明 声明lambda不会抛出异常 可选,一般不用
-> 返回类型 指定返回值类型,编译器通常能自动推导,复杂场景需显式指定 可选
{函数体} 和普通函数的函数体一致 必选

实验代码见下面Sort 函数、优先队列部分。

&在C++中表示引用,意思是取该变量的本身(地址),而不是其副本,一般用于函数,这样在函数内部的修改会被传递回去。

实验代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
void test(int &a){  
    a = 10;  
}  
void solve()  
{  
 int b = 5;  
 cout << b << endl;  
 test(b);  
 cout << b << endl;  
}

Auto类型、结构化绑定

  • auto是 C++11 及后续标准引入的类型推导关键字,简单来说:你声明变量时不用显式写类型,编译器会根据变量的初始化值或上下文自动推导出变量的实际类型,特别是对于迭代器。
  • 结构化绑定是 C++17 新增的核心特性,它的核心作用是将聚合类型(数组、简单结构体 / 类)tuple-like 类型的多个成员 / 元素,一次性解包并绑定到多个变量 中。
1
auto [变量1, 变量2, ...] = 待解包的类型;

迭代器

迭代器是 C++ STL 的核心组件之一,可以把它理解成 “通用化的指针” —— 它封装了对容器(如vectorlistmap)元素的访问逻辑,屏蔽了不同容器的底层实现差异(比如vector是连续数组,list是双向链表),提供了一套统一的语法来遍历、读取、修改容器中的元素。

迭代器类型 核心能力 支持的容器 常用操作
输入迭代器 只读、单向遍历(仅++) 输入流(istream) ++*(读)、==/!=
输出迭代器 只写、单向遍历(仅++) 输出流(ostream) ++*(写)
前向迭代器 可读可写、单向遍历(仅++) forward_list ++*==/!=
双向迭代器 可读可写、双向遍历(++/–) listmapsetmultimap ++--*->==/!=
随机访问迭代器 双向遍历+随机跳转(±n、[]、«/») vectordeque、数组 包含双向迭代器所有操作 + +=n/-=n[]

实验代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void solve()
{
    vector<int> vec = {10, 20, 30, 40};

    // 1. 普通迭代器(可读可写)
    vector<int>::iterator it;
    for (it = vec.begin(); it != vec.end(); ++it) {
        // *it 解引用,获取迭代器指向的元素
        cout << *it << " "; // 输出:10 20 30 40
        *it *= 2; // 修改元素值(普通迭代器支持写)
    }
    cout << endl;

    // 2. const_iterator(只读,不能修改)
    for (vector<int>::const_iterator cit = vec.cbegin(); cit != vec.cend(); ++cit) {
        cout << *cit << " "; // 输出:20 40 60 80
        // *cit *= 2; // 错误:const_iterator只读
    }
    cout << endl;

    // 3. 反向迭代器(从末尾向前遍历)
    for (vector<int>::reverse_iterator rit = vec.rbegin(); rit != vec.rend(); ++rit) {
        cout << *rit << " "; // 输出:80 60 40 20
    }
}

Pair、Tuple、Bool

  • Pair:这是一个二元组,声明为pair<类型1,类型2>,相当于写了一个结构体,类似:
1
2
3
4
struct cusType{
    类型1 first
    类型2 second;
};

实验代码如下:

1
2
3
4
5
6
void solve()
{
    pair<char,int> p;
    p = {'c',2};
    cout << p.first << " " << p.second << endl;
}

p = {'c',2}; 是 C++11 及以上标准中列表初始化结合赋值运算符的用法,本质是:编译器先将初始化列表 {'c', 2} 隐式构造为一个 pair<char, int> 类型的临时对象,然后调用 pair 的赋值运算符,把这个临时对象的值赋给已定义的 pair 对象 p

  • Tuple:这是一个三元组,声明为tuple<类型1,类型2,类型3>,与pair类似,不过其没有firstsecond这种成员函数,三元组的访问一般通过结构化绑定。

实验代码如下:

1
2
3
4
5
6
7
void solve()
{
    tuple<int,char,string> tp;
    tp = {100,'c',"Hello"};
    auto [a,b,c] = tp;
    cout << a << " " << b << " " << c << endl;
}
  • Bool:C++中的bool类型本质上是01的另一种写法,表示为truefalse
1
bool flag = true;

Sort 函数

sort是 C++ STL(标准模板库)<algorithm>头文件中提供的排序函数,它是结合了快速排序、堆排序和插入排序的优点,在平均和最坏情况下的时间复杂度都接近$ O(nlogn) $,所以大部分情况下我们都不需要自己去写排序函数。

排序是不稳定的(若需保持相等元素的相对顺序,改用stable_sort) 。

不稳定:如果排序时有两个相同的元素,排序后其相对位置交换了,则称做不稳定。

一般sort函数的声明如下:

$$ sort(起始迭代器, 结尾迭代器, 比较函数 / 匿名函数); $$

实验代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
bool cmp(pair<int,int> x,pair<int,int> y){
    return x.first < y.first;
}

void solve()
{
    srand(time(0));
    vector<pair<int,int>> vt;
    cout << "初始顺序: ";
    for(int i=1;i<=10;i++){
        int t = rand() % 100 + 1;
        int q = rand() % 100 + 1;
        cout << t << ":" << q << " ";
        vt.push_back({t,q});
    }
    cout << endl;
    sort(vt.begin(),vt.end(),[](auto x,auto y){
        return x.first < y.first;
    });//匿名函数写法
    //sort(vt.begin(),vt.end(),cmp);//比较函数写法一
    //auto cmmp = [](pair<int,int> x,pair<int,int> y)->bool{
    //    return x.first < y.first;
    //};
    //sort(vt.begin(),vt.end(),cmmp);//比较函数写法二
    cout << "排序后: ";
    for(auto [x,y] : vt){
        cout << x << ":" << y << " ";
    }
}

代码模版

一个算竞选手一般有自己的模版,我的模版如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N = 1e6 + 10; //100005
const int inf = 1e18+10;

void solve()
{

}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    int _ = 1;
//  cin >> _ ;
    while(_--)
    {
        solve();
    }
    return 0;
}

STL库

STL(Standard Template Library,标准模板库)是 C++ 编程语言的核心组成部分之一,你可以把它理解成 C++ 为程序员预先封装好的 “万能工具库”—— 里面包含了大量现成的、通用的、高效的数据结构和算法,不用你从头手写这些基础功能,能极大提升开发效率。

  • STL不是面向对象的。为了具有足够通用性,STL主要依赖于模板而不是封装,继承和虚函数(多态性)。
  • STL组件都存在于std命名空间中,使用时需要有命名空间 using namespace std;

Vector 容器

Vector是一个常用的向量容器,其可以看作一个变长数组,要使用Vector,简单的声明形式如下:

$$ vector<数据类型> 容器名(构造参数); $$

其中<>表示泛型,(构造参数)是可选的的。

如果需要在初始化的时候分配内存空间或者声明默认数据值的话,则需要有构造参数,构造参数的格式如下:

$$ vector<数据类型> 容器名(大小,默认值); $$

其中默认值如果不需要的话可以不传。

如果没有声明容器大小的话,其大小默认为0,每次使用成员函数push_back的时候会先申请内存空间再存入元素,否则会直接存入,直到分配的空间用完才会申请。

在某些场景下,频繁使用push_back会因为申请内存空间的时间累计而被某些题目判定为超时。

实验代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
void vtcout(vector<int> &vt){
    cout << "容器大小: " << vt.size() << endl;
    for(auto a : vt) cout << a << " ";
    cout << endl;
}

void solve()
{
    vector<int> vt(10,90);
    vtcout(vt); 

    vt[0] = 100; //将第0个位置的值修改为100
    vtcout(vt);

    vt.push_back(114514); //在容器末尾最后添加一个114514的值
    vtcout(vt);

    vt.push_back(1145);//在容器末尾最后添加一个1145的值
    vtcout(vt);

    vt.pop_back();//删除容器最后一个元素
    vtcout(vt);

    cout << "引用:" << vt.front() << " " << vt.back() << endl; //输出首尾元素
    vt.front() = -100,vt.back() = -114514; //修改容器首尾元素的值
    vtcout(vt);

    int t;cin >> t;
    auto c = find(vt.begin(), vt.end(), t);
    if (c != vt.end()) {
        cout << "找到数字 " << t << "!" << endl;
        int index = c - vt.begin();  // 迭代器相减得到下标
        cout << "它在vector中的下标是:" << index << endl; 
        cout << "迭代器指向的内存地址:" << &*c << endl; //先通过*c解引用迭代器得到元素,再用&取该元素的地址;
    } else {
        cout << "未找到数字 " << t << "!" << endl;
    }

    cout << "容器为空? " << vt.empty() << endl;
    vt.clear();//清空容器
    vtcout(vt);
    cout << "容器为空? " << vt.empty() << endl;
}

Vector几乎是STL中其它所有容器的底层,其它容器基本都能使用Vector的成员函数,常用的有:

  • v.size():返回v中的元素数量。
  • v.empty():判断v中元素是否为空。
  • v.max_size():返回v能容纳的最大元素数量。
  • 比较运算符:==、!=、<、<=、>、>=,比较操作两端的容器必须是同一类型。比较运算将按字典序比较两个容器元素,当所有元素按序相等时,两容器才相等。
  • a=b:用b中元素取代a。
  • a.swap(b)或swap(a,b):交换a、b中的元素。
  • v.insert(pos, e):将元素e的拷贝安插于迭代器pos所指的位置。
  • v.erase(pos):移除pos处的元素。
  • v.erase(begin, end):移除[begin,end)区间内的所有元素。
  • v.clear():移除所有元素

Stack 栈

栈是管理数据的一种结构,体现数据“后进先出”的特性,插入在一端,删除也在这一端,就像一个桶一样,先来的东西在桶底,要取出必须先把上面的东西都取出。

STL实现的栈声明形式如下:

$$ stack<类型> 栈名; $$

其中<>表示泛型,栈的默认大小为0

实验代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
void solve()
{
    stack<int> st;
    cout << "默认大小:" << st.size() << endl;
    cout << "入栈顺序: ";
    for(int i=1;i<=10;i++){
        cout << i << " ";
        st.push(i); //push表示入栈
    }
    cout << endl;
    cout << "大小:" << st.size() << endl;
    cout << "出栈顺序: ";
    while(!st.empty()) {
        cout << st.top() << " "; //top表示取栈顶元素的值
        st.pop(); //pop表示出栈
    }
    cout << endl;
    cout << "大小:" << st.size() << endl;
}

Queue 队列

队列也是管理数据的一种结构,体现数据“先进先出”的特性,插入在一端,删除在另一端。就像排队一样,刚来的人入队要排在队尾,每次出队的都是队首的人。

普通队列

STL实现的普通队列声明形式如下:

$$ queue<类型> 队列名; $$

其中<>表示泛型,队列的默认大小为0

实验代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
void solve()
{
    queue<int> q;
    cout << "默认大小:" << q.size() << endl;
    cout << "入队顺序: ";
    for(int i=1;i<=10;i++){
        cout << i << " ";
        q.push(i); //push表示入队
    }
    cout << endl;
    cout << "大小:" << q.size() << endl;
    cout << "出队顺序: ";
    while(!q.empty()) {
        cout << q.front() << " "; //front表示取队头元素的值
        q.pop(); //pop表示出队
    }
    cout << endl;
    cout << "大小:" << q.size() << endl;
}

优先队列(大/小根堆)

优先队列本质是二叉堆(完全二叉树),它可以分为最大值堆和最小值堆。

最大(小)值堆中,结点一定不小(大)于两个儿子的值。在堆中,两兄弟的大小没有必然联系。最大(小)值堆的根结点是整个树中的最大(小)值。

STL实现的优先队列的简单声明形式如下:

$$ priority\_queue<类型> 优先队列名; $$

这样声明的优先队列是大根堆,其会自动把大的元素排到队首,小的元素排到队尾,实验代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
void solve()
{
    srand(time(0)); //设定随机数种子为当前时间
    priority_queue<int> pq;
    cout << "默认大小:" << pq.size() << endl;
    cout << "入队顺序: ";
    for(int i=1;i<=10;i++){
        int t = rand() % 100 + 1;//取1~100的随机数
        cout << t << " ";
        pq.push(t);
    }
    cout << endl;
    cout << "大小:" << pq.size() << endl;
    cout << "出队顺序: ";
    while(!pq.empty()) {
        cout << pq.top() << " "; 
        pq.pop();
    }
    cout << endl;
    cout << "大小:" << pq.size() << endl;
}

如果需要声明小根堆,其会自动把小的元素排到队首,大的元素排到队尾,一般声明形式如下:

$$ priority\_queue<类型,vector<类型>,greater<类型>> 优先队列名; $$

第一个是必选参数,后两个是可选参数(有默认值)。

  • 第一个参数:<类型>(T,元素类型) : 指定优先队列中存储的元素的类型
  • 第二个参数:vector<类型>(Container,底层容器类型) : 指定优先队列用来存储数据的「底层容器」(优先队列本身是适配器,依赖底层容器实现存储)。
  • 第三个参数:greater<类型>(Compare,比较规则) : 指定优先队列的「优先级判定规则」 。
比较规则 优先级规则 俗称 队列顶端元素
less<类型> 大的元素优先级高 大顶堆(默认) 最大值
greater<类型> 小的元素优先级高 小顶堆 最小值

实验代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
void solve()
{
    srand(time(0));
    priority_queue<int,vector<int>,greater<int>> pq;
    cout << "默认大小:" << pq.size() << endl;
    cout << "入队顺序: ";
    for(int i=1;i<=10;i++){
        int t = rand() % 100 + 1;
        cout << t << " ";
        pq.push(t);
    }
    cout << endl;
    cout << "大小:" << pq.size() << endl;
    cout << "出队顺序: ";
    while(!pq.empty()) {
        cout << pq.top() << " "; 
        pq.pop();
    }
    cout << endl;
    cout << "大小:" << pq.size() << endl;
}

特别的:对于我们自己定义的数据类型,比如结构体,比较规则需要我们自己来定义,一般有两种实现办法。

  • 第一种:重载运算符,这里重载<是因为优先队列需要小于的规则来比较。重载的方法中可以决定顺序,只需要修改return x.pos1 < pos1;中的<>,这里是按pos1排序的小根堆。
1
2
3
4
5
6
7
8
struct cusType{
    int pos1;
    int pos2;

    bool operator<(const cusType x) const {
        return x.pos1 < pos1;
    }
};
  • 第二种:利用std::function(C++11 及以上),这里是按pos1排序的小根堆。
1
2
3
priority_queue<cusType, vector<cusType>, function<bool(cusType,cusType)>> pq([](cusType x, cusType y) { 
    return x.pos1 < y.pos1; 
});

实验代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
struct cusType{
    int pos1;
    int pos2;
    bool operator<(const cusType x) const {
        return x.pos1 < pos1;
    }
};

void solve()
{
    srand(time(0));
//  方法一:
    priority_queue<cusType> pq;
//  方法二:
//    priority_queue<cusType, vector<cusType>, function<bool(cusType,cusType)>> pq([](cusType x, cusType y) { 
//        return x.pos1 < y.pos1; 
//    });
    cout << "默认大小:" << pq.size() << endl;
    cout << "入队顺序: ";
    for(int i=1;i<=10;i++){
        int t = rand() % 100 + 1;
        int q = rand() % 100 + 1;
        cout << t << ":" << q << " ";
        pq.push({t,q});
    }
    cout << endl;
    cout << "大小:" << pq.size() << endl;
    cout << "出队顺序: ";
    while(!pq.empty()) {
        auto [t,q] = pq.top();
        cout << t << ":" << q << " "; 
        pq.pop();
    }
    cout << endl;
    cout << "大小:" << pq.size() << endl;
}

双端队列

dequevector类似,但向deque两端添加或删除元素的开销很小,这对于某些只需要添加元素而不需要删除的场景会有用。

deque的内存管理比较复杂,随机访问的性能不如vector,插入、删除的性能不如list。如果不需要快速地从容器头部插入和删除数据,最好还是用vector

deque特有的操作如下:

1
2
3
4
5
6
7
d[i]//返回d中下标为i的元素的引用。
d.front()//返回第一个元素的引用。
d.back()//返回最后一个元素的引用。
d.pop_back()//删除尾部的元素。该函数没有返回值。
d.pop_front()//删除头部的元素。该函数没有返回值。
d.push_back(e)//在尾部添加一个元素e的副本。
d.push_front(e)//在头部添加一个元素e的副本。

Set 集合

集合(Set)是按照特定顺序存储唯一元素的容器,就是自动排序(从小到大)和去重的容器。

集合在STL中有两种实现,分别是setunordered_set

简单声明形式如下:

$$ set<类型> 集合名;\\ unordered\_set<类型> 集合名; $$
  • set<int> stt:基于红黑树实现。红黑树的特性天然保证了元素的有序性,且操作效率稳定。
  • unordered_set<int> st:基于哈希表实现,通过哈希函数将元素映射到不同的 “桶” 中,没有天然的有序性。
  • 时间复杂度对比:
set unordered_set(平均) unordered_set(最坏)
$ O(log n) $ $ O(1) $ $ O(n) $(哈希冲突严重)
  • set 的效率稳定,因为红黑树的高度是对数级;
  • unordered_set 平均效率更高,但哈希冲突会导致效率退化。
  • unordered_set 内存开销更大
  • set 内存占用更紧凑。
  • set:需要有序元素、追求稳定的最坏时间复杂度、需要快速找最大值 / 最小值(stt.begin() 是最小值,stt.rbegin() 是最大值);
  • unordered_set:仅需去重、不关心顺序、追求更高的平均访问效率(如高频判断元素是否存在)。

实验代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
void solve() {
    unordered_set<int> st;
    set<int> stt;

    // 插入相同元素
    st.insert(5); st.insert(2); st.insert(8); st.insert(2);
    stt.insert(5); stt.insert(2); stt.insert(8); stt.insert(2);

    // 遍历unordered_set 输出顺序不固定
    cout << "unordered_set 遍历:";
    for (int num : st) cout << num << " "; // 例:8 5 2
    cout << endl;

    // 遍历set 输出顺序固定
    cout << "set 遍历:";
    for (int num : stt) cout << num << " "; // 固定:2 5 8
    cout << endl;

    // 查找元素
    if (st.find(5) != st.end()) cout << "unordered_set 找到5\n";
    if (stt.find(5) != stt.end()) cout << "set 找到5\n";
}

Map 映射

映射(map)是关联容器,它按照特定顺序存储由键值和映射值的组合形成的元素。

集合在STL中有两种实现,分别是mapunordered_map

简单声明形式如下:

$$ map<键类型,值类型> 映射名;\\ unordered\_map<键类型,值类型> 映射名; $$
  • map<char, int> mpp:基于红黑树实现,红黑树的特性让键(key)天然保持有序,所有操作的时间复杂度稳定。
  • unordered_map<char, int> mp:基于哈希表实现,通过哈希函数将键映射到哈希表的 “桶” 中,键没有天然的有序性,但平均访问效率更高。
  • 时间复杂度同Set中的说明。
  • map:需要键有序、追求稳定的最坏时间复杂度、需要快速找最大 / 最小键(mp.begin() 是最小键,mp.rbegin() 是最大键);
  • unordered_map:仅需键值映射、不关心键的顺序、追求更高的平均访问效率(如高频通过键查找值)。

实验代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
void solve() {
    unordered_map<char, int> mp;
    map<char, int> mpp;

    // 插入相同键值对(键重复时,后插入的会覆盖前一个)
    mp['c'] = 3; mp['a'] = 1; mp['b'] = 2; mp['a'] = 10; // 'a'的值最终是10
    mpp['c'] = 3; mpp['a'] = 1; mpp['b'] = 2; mpp['a'] = 10; // 'a'的值最终是10

    // 遍历unordered_map 输出顺序不固定
    cout << "unordered_map 遍历:";
    for (auto& pair : mp) cout << pair.first << ":" << pair.second << " "; 
    cout << endl;

    // 遍历map 键严格升序
    cout << "map 遍历:";
    for (auto& pair : mpp) cout << pair.first << ":" << pair.second << " "; 
    cout << endl;

    // 查找键
    if (mp.find('b') != mp.end()) cout << "unordered_map 找到b,值:" << mp['b'] << "\n";
    if (mpp.find('b') != mpp.end()) cout << "map 找到b,值:" << mpp['b'] << "\n";
}

String 字符串类

C++ 字符串操作依赖标准库的 <string> 头文件。

常用操作有:

  • += //字符串相加
  • == != < <= > >= //比较大小
  • s.length()  / s.size()   //字符串长度
  • s.find(str)  //s里找str,返回第一次匹配的位置,没有则返回string::npos
  • s.find(str, pos) //从pos位置开始找  没有返回string::npos
  • s.substr(pos, len) //返回从pos位置开始长度为len的字符串
  • s.substr(pos) //返回pos位置后到结尾的字符串
  • s.replace(pos, len, str)   replace(it1, it2, str)//将s从pos位置长度为len的字符串替换为str
  • s.clear()  //清空
  • s.insert(pos, str)   s.insert(it, it1, it2)//在pos位置插入字符串str
  • s.erase(it)  s.erase(first, last) //擦除

实验代码如下

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
void solve()
{
    // ===================== 1. 字符串初始化 =====================
    cout << "===== 1. 字符串初始化 =====" << endl;
    string s1;                // 空字符串(默认构造)
    string s2 = "Hello C++";  // 从C风格字符串初始化
    string s3(5, 'a');        // 初始化5个连续的'a' → "aaaaa"
    string s4(s2);            // 拷贝构造 → "Hello C++"
    string s5(s2, 6, 3);      // 从s2的第6个字符开始,取3个 → "C++"(索引从0开始)

    cout << "s1: " << s1 << "(空)" << endl;
    cout << "s2: " << s2 << endl;
    cout << "s3: " << s3 << endl;
    cout << "s4: " << s4 << endl;
    cout << "s5: " << s5 << endl;

    // ===================== 2. 基本属性操作 =====================
    cout << "\n===== 2. 基本属性操作 =====" << endl;
    // size()/length():返回字符串长度(字符数)
    cout << "s2长度(size()): " << s2.size() << endl;
    cout << "s2长度(length()): " << s2.length() << endl;
    // empty():判断是否为空字符串(比size()==0更高效)
    cout << "s1是否为空: " << (s1.empty() ? "是" : "否") << endl;
    // capacity():返回当前分配的存储空间大小(字符数,不包含结束符)
    cout << "s2当前容量: " << s2.capacity() << endl;
    // max_size():返回字符串最大可容纳的字符数(系统限制)
    cout << "s2最大可容纳字符数: " << s2.max_size() << endl;
    // resize(n, c):调整字符串长度为n,不足补字符c(默认补'\0')
    s3.resize(3, 'b'); // s3从"aaaaa"变为"aab"
    cout << "s3 resize后: " << s3 << endl;

    // ===================== 3. 字符串拼接 =====================
    cout << "\n===== 3. 字符串拼接 =====" << endl;
    string s6 = "Hello";
    string s7 = " World";
    // 方式1:+ 运算符(最常用,简洁)
    string s8 = s6 + s7;
    cout << "s6 + s7: " << s8 << endl;
    // 方式2:append()函数(更灵活,支持多类型拼接)
    s6.append(s7);       // s6 += s7 → "Hello World"
    cout << "s6.append(s7): " << s6 << endl;
    s6.append(3, '!');   // 追加3个'!' → "Hello World!!!"
    cout << "s6.append(3, '!'): " << s6 << endl;

    // ===================== 4. 字符串比较 =====================
    cout << "\n===== 4. 字符串比较 =====" << endl;
    string s9 = "Apple";
    string s10 = "Banana";
    string s11 = "Apple";
    // 方式1:关系运算符(==、!=、<、>、<=、>=),按ASCII码逐字符比较
    cout << "s9 == s11: " << (s9 == s11 ? "是" : "否") << endl;
    cout << "s9 > s10: " << (s9 > s10 ? "是" : "否") << endl; // 'A'(65) < 'B'(66),结果为否
    // 方式2:compare()函数,返回值:0(相等)、正数(当前串大)、负数(当前串小)
    int cmp_result = s9.compare(s10);
    cout << "s9.compare(s10): " << cmp_result << "(负数表示s9更小)" << endl;

    // ===================== 5. 字符访问 =====================
    cout << "\n===== 5. 字符访问 =====" << endl;
    // 方式1:[]运算符(无越界检查,越界会导致未定义行为)
    char c1 = s2[0]; // 取s2第0个字符 → 'H'
    // 方式2:at()函数(有越界检查,越界抛out_of_range异常)
    char c2 = s2.at(6); // 取s2第6个字符 → 'C'
    cout << "s2[0]: " << c1 << endl;
    cout << "s2.at(6): " << c2 << endl;

    // ===================== 6. 查找与替换 =====================
    cout << "\n===== 6. 查找与替换 =====" << endl;
    string s12 = "I love C++, C++ is cool";
    // find(str, pos):从pos位置(默认0)查找子串,返回首次出现的索引,找不到返回string::npos
    size_t pos = s12.find("C++"); // 查找"C++"首次出现的位置 → 7
    if (pos != string::npos) {
        cout << "\"C++\"首次出现位置: " << pos << endl;
    }
    // rfind(str):从末尾向前查找子串,返回最后一次出现的索引
    size_t rpos = s12.rfind("C++"); // 最后一次出现"C++"的位置 → 11
    cout << "\"C++\"最后出现位置: " << rpos << endl;
    // replace(pos, len, str):从pos位置开始,替换len个字符为str
    s12.replace(pos, 3, "Python"); // 把第一个"C++"替换为"Python"
    cout << "替换后s12: " << s12 << endl;

    // ===================== 7. 插入与删除 =====================
    cout << "\n===== 7. 插入与删除 =====" << endl;
    string s13 = "Hello World";
    // insert(pos, str):在pos位置插入字符串
    s13.insert(5, ","); // 在第5个字符后插入"," → "Hello, World"
    cout << "插入后s13: " << s13 << endl;
    // erase(pos, len):从pos位置开始,删除len个字符
    s13.erase(5, 2); // 从第5个字符开始删2个 → "HelloWorld"
    cout << "删除后s13: " << s13 << endl;

    // ===================== 8. 子串提取 =====================
    cout << "\n===== 8. 子串提取 =====" << endl;
    string s14 = "2026-01-23";
    // substr(pos, len):从pos位置开始,提取len个字符(len省略则取到末尾)
    string year = s14.substr(0, 4);   // 提取年份 → "2026"
    string month = s14.substr(5, 2);  // 提取月份 → "01"
    string day = s14.substr(8);       // 提取日期 → "23"
    cout << "年份: " << year << ", 月份: " << month << ", 日期: " << day << endl;

    // ===================== 9. 类型转换 =====================
    cout << "\n===== 9. 类型转换 =====" << endl;
    // 数字转字符串:to_string()(支持int、double、long等)
    int num = 123;
    double pi = 3.14159;
    string num_str = to_string(num);
    string pi_str = to_string(pi);
    cout << "数字123转字符串: " << num_str << endl;
    cout << "数字3.14159转字符串: " << pi_str << endl;

    // 字符串转数字:stoi(转int)、stol(转long)、stof(转float)、stod(转double)
    string str_num = "456";
    string str_float = "7.89";
    try {
        int n = stoi(str_num);
        double f = stod(str_float);
        cout << "字符串\"456\"转int: " << n << endl;
        cout << "字符串\"7.89\"转double: " << f << endl;
        // 错误示例:字符串无法转数字会抛异常
        // int err = stoi("abc");
    } catch (const invalid_argument& e) {
        cout << "类型转换异常:" << e.what() << endl;
    }

    // ===================== 10. 其他常用操作 =====================
    cout << "\n===== 10. 其他常用操作 =====" << endl;
    string s15 = "test";
    // clear():清空字符串(长度变为0,容量不变)
    s15.clear();
    cout << "s15清空后是否为空: " << (s15.empty() ? "是" : "否") << endl;
    // swap():交换两个字符串的内容
    string s16 = "AAA", s17 = "BBB";
    s16.swap(s17);
    cout << "swap后s16: " << s16 << ", s17: " << s17 << endl;
}
本作品采用知识共享署名-非商业性使用-相同方式共享4.0国际许可协议进行许可(CC BY-NC-SA 4.0)
文章浏览量:Loading
Powered By MC ZBD Studio
发表了57篇文章 · 总计111.20k字
载入天数...载入时分秒...
总浏览量Loading | 访客总数Loading

主题 StackJimmy 设计
由ZephyrBD修改