C++ STL系统整理

目录

一、前言

二、STL大体总览

三、详细整理

一、容器与适配器

一、string

二、vector

 三、stack

 四、set

 五、map


一、前言

  • 学校课程进度太慢,自学的知识又太零碎,所以我决定每周写一篇这样成体系的文章,一来给自己一点压力、也方便记忆查阅,二来也便于后来人一栏CS全貌,方便学习。本套文章重点讲应用,多敲代码,不讲玄而又玄的纯理论,因为各种理论都是在具体的应用场景上提炼出来的,无论如何,根都在代码上。
  • 刚好最近准备参加CCF考试,便找了几篇CCF的题解,发现凡是用C++写的答案,都要用上STL。想来STL也是C++学习者的基本功了。在此将最近搜集的资料整理一下。这篇文章是参考网友的文章而作,也部分参考了C++参考手册
  • 本文过长,需善用浏览器搜索功能,如果您感到不适,请点击   每周系统CS|第一周:C++ STL模板库系统讲解(多文章版) 
  • 笔者也是初学,疏漏之处望批评指正。
  • 参考:王刚博客

二、STL大体总览

  1. STL是什么:Standard Template Library,标准模板库,就是大牛们用c++的模板机制写好的一整套供我们使用的库文件。图灵奖N.Wirth(沃斯)说得好:程序 = 算法 + 数据结构,这STL大概就是其中的数据结构了。
  2. STL怎么用:STL就是一堆写好的头文件,我们在使用时只需include一下自己需要的文件就OK,注意,引用STL是不需要加“.h”后缀。
  3. STL包含六大类:
    1. 容器(containers):容器是C++的机制,数组是C++唯一提供原生支持的容器。STL使用类模板的方式提供多种容器,使我们可以方便的进行各种数据存取。
    2. 适配器(adapters):以序列式容器为基础,提供的栈,队列和优先级队列等高级容器。
    3. 迭代器(iterators):类似于指针,每种容器有相应迭代器,它们用来统一容器的操作方式。
    4. 算法(algorithm):包含一系列常见算法。
    5. 空间配置器(allocator):其中主要工作包括两部分:1,对象的创建与销毁。2,内存的创建与释放。
    6. 仿函数(functor):仿函数又称为函数对象,其实就是重载了()操作符的struct,没有什么特别的地方。

接下来,我们将逐步学习这些优秀工具的使用方法。由于容器与适配器非常相似,所以我将他们合并,统一放到“容器”里来讲。

三、详细整理

 

一、容器与适配器

在总览中提到,容器就是存放数据的高级数据结构,数组是C++唯一提供原生支持的容器。STL中提供的容器(和适配器)有:

#include

Container Class

<vector>  //序列式

vector, vector<bool>

<deque>  //序列式

deque

<list>  //序列式

list

<map>  //关联式

map, multimap

<set>   //关联式

set, multiset

<stack>  //适配

stack

<queue>  //适配

queue, priority_queue 

<string>

string

 

 

序列式容器:内部元素是按照写入顺序排列的。

  1. vector(向量):自由的数组,底层数据结构就是数组,自动分配内存,可以像数组一样用索引(下标)直接存取元素,数组的尾部添加和移除元素很快,但在头部和中部插入元素比较耗时。
  2. deque(双端队列):更加自由的数组,底层数据结构还是数组,可以像数组一样用索引(下标)直接存取元素,在数组的头部和尾部插入和删除元素很快。
  3. list(列表):底层数据结构是双向链表,不能使用索引存取数据元素(需要按顺序走到要存取的元素),在任何位置插入和删除都很快,只需要简单的移动一下指针。

关联式容器:元素位置取决于特定的排序准则,和插入的顺序无关,底层数据结构为二叉树。

  1. set(集合):内部元素依据其值自动排序,set内相同的数值元素只能出现一次。
  2. multiset(多重集合):也即允许重复值出现的集合。
  3. map(映射):也即“提供索引”的集合,元素是成对的键值对,内部元素的值依据键自动排序,键只允许出现一次。
  4. multimap(多重映射):允许键对出现多次的map。

适配器:也即高级的序列式容器,出于功能需要,没有迭代器。

  1. stack(栈):先进后出,可以使用序列式容器中的vector,deque,list中的任意一种作为其底层的数据结构。默认基于deque。
  2. queue(队列):先进先出,队列可以使用deque和list中的任意一种作为其底层的数据结构。默认基于deque。
  3. priority_queue(优先队列):对元素进行自动排序的队列,可以使用vector和deque来实现其底层结构,默认基于vector。

 

一、字符串 string

  • string是STL封装的一个类,是一个容器。
  • string不需要考虑内存,string会自动管理内存。
  • string提供了一些列的成员函数,简化了字符串操作。

一、字符的初始化

// 构造一个空字符串
string str;
 
//用双引号初始化字符串
string str("HelloString");
string str = "HelloString";

// 重复某个字符n次
string str(10,'A');

//基于已有字符串初始化
string str(str1);
string str = str1;

二、操作符重载函数

1.赋值操作符(=)

str = "Hello!";
str = str2;

2.移位操作符(<< >>)  

cout << str <<endl;//输出字符串
cin >> str;//标准输入到字符串str

3.数组下标操作符([]) 与数组操作相同

char c = str[5];    //取得某值
str[5] = 'A';    //赋值

4.加法运算符(+)

str1 = str2 + "World";//将字符串str2和"World"相加并赋值给str1
//注意:+左右两边需至少一边为字符串类型


5.加法赋值运算符(+=)
str1 += str2;//将字符串str1和str2相加,赋值给str1

三、常用成员函数

int len = str.length();    //获取字符串长度
bool isEmpty = str.empty();     //字符串是否为空,空返回1,非空返回0
bool result = str.compare(str2);    //对比字符串,相同返回0,不同返回1
string str = str.substr(int pos, int num);    //获取下标pos开始num个字符
int location = str.find(char* str,int pos);    //返回str的首字符索引,从pos开始往后查找,没有则返回-1 
rfind(char* str,int pos);    //返回字符串str的首字符索引,从pos位置开始往前查找,没有返回-1 

str.replace(int pos, int num, string str1);//将从索引pos开始的num个字符替换为str1

str.erase(int pos, int num); // 删除str从索引pos开始的num个字符

str.insert(int pos, string str1); // 在索引pos之后插入str1
str.insert(int pos, int times, char ch); // 在索引pos之后插入字符ch times次

str.assign(string str1);            //...
str.assign(string str1, int num);       //将str1开头的前num个字符赋值给str
str.assign(string str1, int pos, int num);  //将str1从pos开始num个字符...
str.assign(int times, char 'c');         //...

str.append(string str1);            //向str尾部追加str1
str.append(string str1, int pos, int num);  // str追加str2的从第pos个开始的num个字符 
str.append(int times, char ch);       // str追加times个字符ch 

二、vector

  • vector是有程序自主管理长度的数组,即自由的数组。
  • vector支持索引(下标)存取。
  • vector容器在尾部插入和删除数据比较快,在中部或者头部插入或删除元素比较费时。
  • 头文件:# include<vector>

一、初始化

1.vector支持多种类型

// 定义一个存放int类型的向量容器
vector<int> v1;
// 定义存放一个double类型的向量容器
vector<double> v2;
// 定义一个存放string类型的向量容器
vector<string> v3;
// 定义一个存放自定义类型的向量容器,该类型必须提供拷贝构造函数,因为容器的存放是按值复制的方式
vector<Student> v4;

2.vector的有参构造函数

// 使用数组初始化vector
int array[] = { 1,2,3,4,5,6,7,8,9 };
vector<int> v1(array, array + 5);
// 使用vector初始化vector
vector<int> v2(v1.begin(), v1.begin() + 3);
// 使用n个相同的元素初始化vector
vector<char> v3(n, 'A');

3.vector的拷贝构造函数

// 拷贝构造函数
vector<char> v2 = v1;

二、操作符重载函数

1.赋值操作符

// 赋值操作符重载
v2 = v1;

2.数组下标操作符

// 获取第i个元素
char c2 = v[i];
// 修改第二个元素
v[i] = 'X';

三、成员函数

1.vector尾部添加和尾部删除

// vector的尾部添加元素
v.push_back('A');
// vector的尾部删除元素
v.pop_back();

2.vector获取头部和尾部元素

// 获取vector的头元素和尾部元素
char first = v.front();
char last = v.back();

 

3.vector的长度

// 获取vector的长度
int v_size = v.size();

4.vector是否为空及清空元素操作

// 判断vector容器是否为空
bool isEmpty = v2.empty();
// vector容器的元素
v2.clear();

5.vector元素的获取和修改

// 获取第二个元素 
char c2 = v[2]; 
// 修改第二个元素 
v[2] = 'X';

6.vector的遍历

// 增强for遍历
for (int tmp : v1) {
    cout << tmp << " ";
}

// 迭代器正向遍历
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
    *it += 1;
    cout << *it << " ";
}

// 迭代器逆向遍历
for (vector<int>::reverse_iterator it = v1.rbegin(); it != v1.rend(); it++) {
    cout << *it << " ";
}

 

7.vector的插入

// 创建vector容器
vector<char> v(10, 'A');
// 在vector容器的第二个位置插入字符'X'
v.insert(v.begin() + 2, 'X');
// 在vector容器的最后位置插入5个'B'
v.insert(v.end(), 5, 'B');

8.vector的删除

// 删除第it个元素 
v.erase(iterator it);
// 删除两个iterator之间的字符 
v.erase(iterator it1,iterator it2);

 

五,vector容器添加自定义数据类型

1.定义老师类

#pragma once
# include<iostream>
using namespace std;
/* 定义老师类 */
class Teacher
{
private:
    char * name;
    int age;
public:
    Teacher();
    Teacher(char * name, int age);
    /* 如果往容器中存入数据,必须定义拷贝构造函数 */
    Teacher(const Teacher& teacher);
    ~Teacher();
public:
    friend ostream& operator<<(ostream& out, Teacher& teacher);
};

 

2.老师类的实现

# define _CRT_SECURE_NO_WARNINGS
# include<iostream>
# include"Teacher.h"
using namespace std;
/* 无参构造 */
Teacher::Teacher()
{
    this->name = NULL;
    this->age = 0;
}
/* 有参构造 */
Teacher::Teacher(char * name, int age)
{
    this->name = new char[strlen(name)+1];
    strcpy(this->name, name);
    this->age = age;
}
/* 拷贝构造函数 */
Teacher::Teacher(const Teacher& teacher)
{
    this->name = new char[strlen(teacher.name)+1];
    strcpy(this->name, teacher.name);
    this->age = teacher.age;
}
/* 析构函数 */
Teacher::~Teacher()
{
    if (this->name != NULL)
    {
        delete [] this->name;
        this->name = NULL;
        this->age = 0;
    }
}
/* 友元函数:重载左移操作符 */
ostream& operator<<(ostream& out, Teacher& teacher)
{
    cout << teacher.name << " = " << teacher.age;
    return out;
}

 

3.向量容器存入老师对象

# include<iostream>
# include<vector>
# include"Teacher.h"
using namespace std;

int main()
{
    // 定义容器
    vector<Teacher> v;
    // 定义5个老师对象
    Teacher t1("刘备", 56);
    Teacher t2("关羽", 45);
    Teacher t3("张飞", 34);
    Teacher t4("赵云", 30);
    Teacher t5("马超", 25);
    // 存入向量容器
    v.push_back(t1);
    v.push_back(t2);
    v.push_back(t3);
    v.push_back(t4);
    v.push_back(t5);
    // 遍历
    for (Teacher t : v)
    {
        cout << t << endl;
    }

    return 0;
}

 三、stack

  • Stack(堆栈) 提供了堆栈的全部功能,是先进后出(FILO)的数据结构。
  • 常用于代替函数递归,以降低资源消耗。
  • #include <stack> 

一、stack声明与初始化

stack<int> s = s1;

二、c++ stl stack的成员函数

s.empty();  //堆栈为空则返回真
s.push();   //在栈顶增加元素
s.top();    //返回栈顶元素
s.pop();    //移除栈顶元素
s.size();   //返回栈中元素数目

四、set

 

  • set是一个集合容器,其中所包含的元素是唯一的,集合中的元素按照一定的顺序排列,元素是按照排序规则插入,不能指定插入位。
  • set的底层数据结构是红黑二叉树,红黑树属于平衡二叉树。在插入操作和删除操作比vector快。
  • set不能使用下标存取,需要使用迭代器。
  • multiset允许插入多个重复的元素。
  • 不可以直接修改set和multiset容器中的元素值,因为该类容器是自动排序的,如果想要修改某个元素的值,需要先删除该元素,然后再重新插入新的元素。
  • # include<set>。

二,set和multiset的代码示例

1.set和multiset的基本比较

复制代码

# include<iostream>
# include<set>
# include<string>

using namespace std;

int main()
{
    // 定义set集合
    set<string> s1;
    // 定义multiset集合
    multiset<string> s2;
    
    // 往set集合中插入元素,其返回结果是一个pair,包含插入进去后的迭代器以及是否插入成功的标志
    pair<set<string>::iterator, bool> p1 =  s1.insert("Hello");
    cout << *p1.first << "," << p1.second << endl;
    // 我们发现往set集合中插入相同的元素会插入不成功
    pair<set<string>::iterator, bool> p2 = s1.insert("Hello");
    cout << *p2.first << "," << p2.second << endl;
    // 遍历
    for (string tmp : s1)
    {
        cout << tmp << endl;
    }
    // 我们往multiset集合中插入元素
    s2.insert("Hello");
    s2.insert("Hello");
    for (string tmp : s2)
    {
        cout << tmp << endl;
    }

    return 0;
}

复制代码

2.set集合的遍历

复制代码

# include<iostream>
# include<string>
# include<set>
using namespace std;

int main02()
{
    // 定义集合
    set<string> s;
    // 插入集合
    s.insert("Hello");
    s.insert("world");
    s.insert("C++");
    // 增强for遍历
    for (string tmp : s)
    {
        cout << tmp << " ";
    }
    cout << endl;
    // 迭代器正向遍历
    for (set<string>::iterator it = s.begin(); it != s.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
    // 迭代器逆向遍历
    for (set<string>::reverse_iterator it = s.rbegin(); it != s.rend(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;

    return 0;
}

复制代码

3.set集合的删除

复制代码

# include<iostream>
# include<set>
# include<string>

using namespace std;

int main03()
{
    // 创建集合
    set<string> s;
    // 插入元素
    s.insert("Hello");
    s.insert("World");
    s.insert("Unreal");
    s.insert("C++");
    // 删除元素World
    for (set<string>::iterator it = s.begin(); it != s.end();)
    {
        if (*it == "World")
        {
            it = s.erase(it);
        }
        else {
            it++;
        }
    }
    // 输出集合的长度
    int size1 = s.size();
    cout << "size1 = " << size1 << endl;
    // 遍历元素删除
    set<string>::iterator it = s.begin();
    while (!s.empty())
    {
        cout << *it << endl;
        it = s.erase(it);
    }
    // 输出集合的长度
    int size2 = s.size();
    cout << "size = " << size2 << endl;

    return 0;
}

复制代码

4.set常用的函数

    s.insert(3);
    s.insert(2);
    s.insert(0);
    s.insert(1);
    // 高级for遍历元素
    for (int tmp : s)
    {
        cout << tmp << " ";
    }
    
    // 统计元素出现的次数
    int cs = s.count(0);
    
    // 查找元素为2的迭代器
    set<int>::iterator it = s.find(2);

5.自定义函数对象实现对学生类的排序

# include<iostream>
# include<string>
# include<set>
using namespace std;
/*
    定义学生类,按照其年龄进行排序
*/
class Student
{
public:
    string name;
    int age;
public:
    Student(string name, int age)
    {
        this->name = name;
        this->age = age;
    }
};
/* 重载了()操作符
    定义函数对象:所谓的函数对象,就是的结构体
*/
struct StudentFunctor
{
    bool operator()(const Student& stu1,const Student& stu2)
    {
        // 如果是>则是从大到小排序,如果是<则是从小到大排序
        return stu1.age > stu2.age;
    }
};


int main()
{
    // 定义学生集合
    set<Student,StudentFunctor> s;
    // 插入学生
    s.insert(Student("张飞",34));
    s.insert(Student("关羽", 45));
    s.insert(Student("赵云", 30));
    s.insert(Student("马超", 21));
    // 打印学生
    for (Student stu : s)
    {
        cout << stu.name << " = " << stu.age << endl;
    }

    return 0;

 五、map

  • map是关联式容器,一个map是一个键值对序列,提供基于key的快速检索能力。
  • map中key值是唯一的,集合中的元素按照一定的顺序排列,元素插入过程是按照排序规则插入的,不能指定插入位置。
  • map的底层数据结构是红黑树变体的平衡二叉树。在插入和删除操作中比vector要快。
  • map可以直接存取key所对应得value,支持[]操作符,如map[key] = value;
  • multimap和map的区别:
    • map中每个键只能出现一次,而multimap中相同的键可以出现多次
    • map支持 [ ] 操作符,multimap不支持 [ ] 操作符。
  • # include<map>

一、map和multimap的初始化

// map的无参构造函数
map<string,string> m1;
// multimap的无参构造函数
multimap<string, string> m2;

二、运算符重载

//插入值  multimap不支持[]操作
m["nokia"] = "Qt";
//修改值
m["nokia"] = "NewQt";
?multimap插入怎样?

三、成员函数

1.获取属性而不修改内容

//统计key,map可能结果为0或者1,multimap结果多变
int s = m.count("aaa");

//查找,根据key来查找,返回该key的迭代器  multimap返回第一个找到的值,it++是下一个
map<string,string>::iterator it = m.find("aaa");

//获取map的大小
int size = m.size();

 

2.修改map内容

//删除操作
map<string, string>::iterator it = m.begin();
it = m.erase(it);

/* 插入操作 */
m.insert(make_pair("aaa", "AAA"));
m.insert(make_pair("bbb", "BBB"));
// 插入方式1,返回结果是是否插入成功
pair<map<string,string>::iterator,bool> p1 = m.insert(pair<string, string>("Epic", "UnrealEngine"));
// 插入方式2,返回结果是是否插入成功
pair<map<string, string>::iterator, bool> p2 = m.insert(make_pair("Unity", "Unity3D"));
// 插入方式3,返回结果是是否插入成功
pair<map<string, string>::iterator, bool> p3 = m.insert(map<string, string>::value_type("Oracle", "Java"));
// 插入方式4
key下标//multimap不支持

//清除内容
m.clear();

四、迭代器

// 正向遍历 
for (map<string, string>::iterator it = m.begin(); it != m.end(); it++) { 
    cout << it->first << " = " << it->second << endl; 
} 
// 反向遍历 
for (map<string, string>::reverse_iterator it = m.rbegin(); it != m.rend(); it++) { 
    cout << it->first << " = " << it->second << endl; 
} 

//查找
//根据key来查找,返回该key的迭代器
//multimap返回找到的第一个键值对,it++是下一个找到的键值对
map<string,string>::iterator it = m.find("aaa");

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值