c++:STL容器及其接口(string、vector、deque、stack、queue、list、set/multiset、map/multimap)

STL(Standard Template Library,标准模板库)


目录

一、STL 六大组件简介

二、 string容器

2.1 string容器基本概念

2.2 string 构造函数

2.3 string基本赋值操作

2.4 string存取字符操作

2.5 string拼接操作

2.6 string查找和替换

2.7 string比较操作

2.8 string子串

2.9 string插入和删除操作

2.10 string和c-style字符串转换

三、vector容器

3.1 vector构造函数

3.2 vector常用赋值操作

3.3 vector大小操作

3.4 vector数据存取操作

3.5 vector插入和删除操作

3.6 巧用swap,收缩内存空间

3.7 reserve预留空间

四、deque容器

4.1 deque容器实现原理

4.2 deque构造函数

4.3 deque赋值操作

4.4 deque大小操作

4.5 deque双端插入和删除操作

4.6 deque数据存取

4.7 deque插入操作

4.8 deque删除操作

五、stack容器

5.1stack构造函数

5.2 stack赋值操作

5.3 stack数据存取操作

5.4 stack大小操作

六、queue容器

6.1 queue构造函数

6.2 queue存取、插入和删除操作

6.3 queue赋值操作

6.4 queue大小操作

七、list容器

7.1 list构造函数

7.2 list数据元素插入和删除操作

7.3 list大小操作

7.4 list赋值操作

7.5 list数据的存取

7.6 list反转排序

八、set/multiset容器

8.1 set构造函数

8.2 set赋值操作

8.3 set大小操作

8.4 set插入和删除操作

8.5 set查找操作

8.6  对组(pair)

九、map/multimap容器

9.1 map构造函数

9.2 map赋值操作

9.3 map大小操作

9.4 map插入数据元素操作

9.5 map删除操作

9.6 map查找操作


 

一、STL 六大组件简介

 

STL提供了六大组件,彼此之间可以组合套用,这六大组件分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器。

  • 容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据,从实现角度来看,STL容器是一种class template。
  • 算法:各种常用的算法,如sort、find、copy、for_each。从实现的角度来看,STL算法是一种function tempalte.
  • 迭代器:扮演了容器与算法之间的胶合剂,共有五种类型,从实现角度来看,迭代器是一种将operator* , operator-> , operator++,operator--等指针相关操作予以重载的class template. 所有STL容器都附带有自己专属的迭代器,只有容器的设计者才知道如何遍历自己的元素。原生指针也是一种迭代器
  • 仿函数:行为类似函数,可作为算法的某种策略。从实现角度来看,仿函数是一种重载了operator()的class 或者class template
  • 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。
  • 空间配置器:负责空间的配置与管理。从实现角度看,配置器是一个实现了动态空间配置、空间管理、空间释放的class tempalte.

 

二、 string容器

2.1 string容器基本概念

C风格字符串(以空字符结尾的字符数组)太过复杂难于掌握,不适合大程序的开发,所以C++标准库定义了一种string类,定义在头文件<string>。

String和c风格字符串对比:

  • Char*是一个指针,String是一个类

string封装了char*,管理这个字符串,是一个char*型的容器。

  • String封装了很多实用的成员方法

查找find,拷贝copy,删除delete 替换replace,插入insert

  • 不用考虑内存释放和越界

 string管理char*所分配的内存。每一次string的复制,取值都由string类负责维护,不用担心复制越界和取值越界等。

 

2.2 string 构造函数

string();//创建一个空的字符串 例如: string str;     

string(const string& str);//使用一个string对象初始化另一个string对象

string(const char* s);//使用字符串s初始化

string(int n, char c);//使用n个字符c初始化

 

2.3 string基本赋值操作

string& operator=(const char* s);//char*类型字符串 赋值给当前的字符串

string& operator=(const string &s);//把字符串s赋给当前的字符串

string& operator=(char c);//字符赋值给当前的字符串

string& assign(const char *s);//把字符串s赋给当前的字符串

string& assign(const char *s, int n);//把字符串s的前n个字符赋给当前的字符串

string& assign(const string &s);//把字符串s赋给当前字符串

string& assign(int n, char c);//用n个字符c赋给当前字符串

string& assign(const string &s, int start, int n);//将s从start开始n个字符赋值给字符串

 

2.4 string存取字符操作

char& operator[](int n);//通过[]方式取字符

char& at(int n);//通过at方法获取字符

 

2.5 string拼接操作

string& operator+=(const string& str);//重载+=操作符

string& operator+=(const char* str);//重载+=操作符

string& operator+=(const char c);//重载+=操作符

string& append(const char *s);//把字符串s连接到当前字符串结尾

string& append(const char *s, int n);//把字符串s的前n个字符连接到当前字符串结尾

string& append(const string &s);//同operator+=()

string& append(const string &s, int pos, int n);//把字符串s中从pos开始的n个字符连接到当前字符串结尾

string& append(int n, char c);//在当前字符串结尾添加n个字符c

 

2.6 string查找和替换

int find(const string& str, int pos = 0) const; //查找str第一次出现位置,从pos开始查找

int find(const char* s, int pos = 0) const//查找s第一次出现位置,从pos开始查找

int find(const char* s, int pos, int n) const//从pos位置查找s的前n个字符第一次位置

int find(const char c, int pos = 0) const//查找字符c第一次出现位置

int rfind(const string& str, int pos = npos) const;//查找str最后一次位置,从pos开始查找

int rfind(const char* s, int pos = npos) const;//查找s最后一次出现位置,从pos开始查找

int rfind(const char* s, int pos, int n) const;//从pos查找s的前n个字符最后一次位置

int rfind(const char c, int pos = 0) const; //查找字符c最后一次出现位置

string& replace(int pos, int n, const string& str); //替换从pos开始n个字符为字符串str

string& replace(int pos, int n, const char* s); //替换从pos开始的n个字符为字符串s

 

2.7 string比较操作

/*

compare函数在>时返回 1,<时返回 -1,==时返回 0。

比较区分大小写,比较时参考字典顺序,排越前面的越小。

大写的A比小写的a小。

*/

int compare(const string &s) const;//与字符串s比较

int compare(const char *s) const;//与字符串s比较

2.8 string子串

string substr(int pos = 0, int n = npos) const;//返回由pos开始的n个字符组成的字符串

 

2.9 string插入和删除操作

string& insert(int pos, const char* s); //插入字符串

string& insert(int pos, const string& str); //插入字符串

string& insert(int pos, int n, char c);//在指定位置插入n个字符c

string& erase(int pos, int n = npos);//删除从Pos开始的n个字符

 

 

2.10 string和c-style字符串转换

//string 转 char*

string str = "itcast";

const char* cstr = str.c_str();

//char* 转 string

char* s = "itcast";

string str(s);

提示:

  •    为了修改string字符串的内容,下标操作符[]和at都会返回字符的引用。但当字符串的内存被重新分配之后,可能发生错误.

 

    string s = "abcdefg";

    char& a = s[2];

    char& b = s[3];

 

    a = '1';

    b = '2';

 

    cout << s << endl;

    cout << (int*)s.c_str() << endl;

 

    s = "pppppppppppppppppppppppp";

 

    //a = '1';

    //b = '2';

 

    cout << s << endl;

    cout << (int*)s.c_str() << endl;

 

三、vector容器

vector的数据安排以及操作方式,与array非常相似,两者的唯一差别在于空间的运用的灵活性。Array是静态空间,一旦配置了就不能改变,要换大一点或者小一点的空间,可以,一切琐碎得由自己来,首先配置一块新的空间,然后将旧空间的数据搬往新空间,再释放原来的空间。Vector是动态空间。

Vector维护一个线性空间,所以不论元素的型别如何,普通指针都可以作为vector的迭代器,因为vector迭代器所需要的操作行为,如operaroe*, operator->, operator++, operator--, operator+, operator-, operator+=, operator-=, 普通指针天生具备。Vector支持随机存取,而普通指针正有着这样的能力。所以vector提供的是随机访问迭代器

  •    所谓动态增加大小,并不是在原空间之后续接新空间(因为无法保证原空间之后尚有可配置的空间),而是一块更大的内存空间,然后将原数据拷贝新空间,并释放原空间。因此,对vector的任何操作,一旦引起空间的重新配置,指向原vector的所有迭代器就都失效了。这是程序员容易犯的一个错误,务必小心。

 

3.1 vector构造函数

vector<T> v; //采用模板实现类实现,默认构造函数

vector(v.begin(), v.end());//将v[begin(), end())区间中的元素拷贝给本身。

vector(n, elem);//构造函数将n个elem拷贝给本身。

vector(const vector &vec);//拷贝构造函数。

 

//例子 使用第二个构造函数 我们可以...

int arr[] = {2,3,4,1,9};

vector<int> v1(arr, arr + sizeof(arr) / sizeof(int));

 

3.2 vector常用赋值操作

assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。

assign(n, elem);//将n个elem拷贝赋值给本身。

vector& operator=(const vector  &vec);//重载等号操作符

swap(vec);// 将vec与本身的元素互换。

 

3.3 vector大小操作

size();//返回容器中元素的个数

empty();//判断容器是否为空

resize(int num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。

resize(int num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长>度的元素被删除。

capacity();//容器的容量

reserve(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问。

 

3.4 vector数据存取操作

at(int idx); //返回索引idx所指的数据,如果idx越界,抛出out_of_range异常。

operator[];//返回索引idx所指的数据,越界时,运行直接报错

front();//返回容器中第一个数据元素

back();//返回容器中最后一个数据元素

 

3.5 vector插入和删除操作

insert(const_iterator pos, int count,ele);//迭代器指向位置pos插入count个元素ele.

push_back(ele); //尾部插入元素ele

pop_back();//删除最后一个元素

erase(const_iterator start, const_iterator end);//删除迭代器从start到end之间的元素

erase(const_iterator pos);//删除迭代器指向的元素

clear();//删除容器中所有元素

 

3.6 巧用swap,收缩内存空间

#define _CRT_SECURE_NO_WARNINGS

#include<iostream>

#include<vector>

using namespace std;

 

int main(){

 

    vector<int> v;

    for (int i = 0; i < 100000;i ++){

        v.push_back(i);

    }

 

    cout << "capacity:" << v.capacity() << endl;

    cout << "size:" << v.size() << endl;

 

    //此时 通过resize改变容器大小

    v.resize(10);

 

    cout << "capacity:" << v.capacity() << endl;

    cout << "size:" << v.size() << endl;

 

    //容量没有改变

    vector<int>(v).swap(v);

 

    cout << "capacity:" << v.capacity() << endl;

    cout << "size:" << v.size() << endl;

 

 

    system("pause");

    return EXIT_SUCCESS;

}

 

3.7 reserve预留空间

#define _CRT_SECURE_NO_WARNINGS

#include<iostream>

#include<vector>

using namespace std;

 

int main(){

 

    vector<int> v;

 

    //预先开辟空间

    v.reserve(100000);

 

    int* pStart = NULL;

    int count = 0;

    for (int i = 0; i < 100000;i ++){

        v.push_back(i);

        if (pStart != &v[0]){

            pStart = &v[0];

            count++;

        }

    }

 

    cout << "count:" << count << endl;

 

    system("pause");

    return EXIT_SUCCESS;

}

 

四、deque容器

Vector容器是单向开口的连续内存空间,deque则是一种双向开口的连续线性空间。所谓的双向开口,意思是可以在头尾两端分别做元素的插入和删除操作,当然,vector容器也可以在头尾两端插入元素,但是在其头部操作效率奇差,无法被接受。

 

4.1 deque容器实现原理

queue没有迭代器Queue所有元素的进出都必须符合”先进先出”的条件,只有queue的顶端元素,才有机会被外界取用。Queue不提供遍历功能,也不提供迭代器。

Deque容器是连续的空间,至少逻辑上看来如此,连续现行空间总是令我们联想到array和vector,array无法成长,vector虽可成长,却只能向尾端成长,而且其成长其实是一个假象,事实上(1) 申请更大空间 (2)原数据复制新空间 (3)释放原空间 三步骤,如果不是vector每次配置新的空间时都留有余裕,其成长假象所带来的代价是非常昂贵的。

Deque是由一段一段的定量的连续空间构成。一旦有必要在deque前端或者尾端增加新的空间,便配置一段连续定量的空间,串接在deque的头端或者尾端。Deque最大的工作就是维护这些分段连续的内存空间的整体性的假象,并提供随机存取的接口,避开了重新配置空间,复制,释放的轮回,代价就是复杂的迭代器架构。

既然deque是分段连续内存空间,那么就必须有中央控制,维持整体连续的假象,数据结构的设计及迭代器的前进后退操作颇为繁琐。Deque代码的实现远比vector或list都多得多。

Deque采取一块所谓的map(注意,不是STL的map容器)作为主控,这里所谓的map是一小块连续的内存空间,其中每一个元素(此处成为一个结点)都是一个指针,指向另一段连续性内存空间,称作缓冲区。缓冲区才是deque的存储空间的主体。

 

4.2 deque构造函数

deque<T> deqT;//默认构造形式

deque(beg, end);//构造函数将[beg, end)区间中的元素拷贝给本身。

deque(n, elem);//构造函数将n个elem拷贝给本身。

deque(const deque &deq);//拷贝构造函数。

 

4.3 deque赋值操作

assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。

assign(n, elem);//将n个elem拷贝赋值给本身。

deque& operator=(const deque &deq); //重载等号操作符

swap(deq);// 将deq与本身的元素互换

 

4.4 deque大小操作

deque.size();//返回容器中元素的个数

deque.empty();//判断容器是否为空

deque.resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。

deque.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除。

 

4.5 deque双端插入和删除操作

push_back(elem);//在容器尾部添加一个数据

push_front(elem);//在容器头部插入一个数据

pop_back();//删除容器最后一个数据

pop_front();//删除容器第一个数据

 

4.6 deque数据存取

at(idx);//返回索引idx所指的数据,如果idx越界,抛出out_of_range。

operator[];//返回索引idx所指的数据,如果idx越界,不抛出异常,直接出错。

front();//返回第一个数据。

back();//返回最后一个数据

4.7 deque插入操作

insert(pos,elem);//在pos位置插入一个elem元素的拷贝,返回新数据的位置。

insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值。

insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值。

4.8 deque删除操作

clear();//移除容器的所有数据

erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。

erase(pos);//删除pos位置的数据,返回下一个数据的位置。

 

五、stack容器

stack是一种先进后出(First In Last Out,FILO)的数据结构,它只有一个出口,形式如图所示。stack容器允许新增元素,移除元素,取得栈顶元素,但是除了最顶端外,没有任何其他方法可以存取stack的其他元素。换言之,stack不允许有遍历行为。

stack没有迭代器,Stack所有元素的进出都必须符合”先进后出”的条件,只有stack顶端的元素,才有机会被外界取用。Stack不提供遍历功能,也不提供迭代器。

5.1stack构造函数

stack<T> stkT;//stack采用模板类实现, stack对象的默认构造形式:

stack(const stack &stk);//拷贝构造函数

5.2 stack赋值操作

stack& operator=(const stack &stk);//重载等号操作符

5.3 stack数据存取操作

push(elem);//向栈顶添加元素

pop();//从栈顶移除第一个元素

top();//返回栈顶元素

5.4 stack大小操作

empty();//判断堆栈是否为空

size();//返回堆栈的大小

 

六、queue容器

Queue是一种先进先出的数据结构,它有两个出口,queue容器允许从一端新增元素,从另一端移除元素。

Queue所有元素的进出都必须符合”先进先出”的条件,只有queue的顶端元素,才有机会被外界取用。Queue不提供遍历功能,也不提供迭代器。

6.1 queue构造函数

queue<T> queT;//queue采用模板类实现,queue对象的默认构造形式:

queue(const queue &que);//拷贝构造函数

 

6.2 queue存取、插入和删除操作

push(elem);//往队尾添加元素

pop();//从队头移除第一个元素

back();//返回最后一个元素

front();//返回第一个元素

 

6.3 queue赋值操作

queue& operator=(const queue &que);//重载等号操作符

 

6.4 queue大小操作

empty();//判断队列是否为空

size();//返回队列的大小

 

七、list容器

List容器是一个双向链表。

链表是一种物理存储单元上非连续、非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

 

  • 采用动态存储分配,不会造成内存浪费和溢出
  • 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
  • 链表灵活,但是空间和时间额外耗费较大

 

7.1 list构造函数

list<T> lstT;//list采用采用模板类实现,对象的默认构造形式:

list(beg,end);//构造函数将[beg, end)区间中的元素拷贝给本身。

list(n,elem);//构造函数将n个elem拷贝给本身。

list(const list &lst);//拷贝构造函数。

7.2 list数据元素插入和删除操作

push_back(elem);//在容器尾部加入一个元素

pop_back();//删除容器中最后一个元素

push_front(elem);//在容器开头插入一个元素

pop_front();//从容器开头移除第一个元素

insert(pos,elem);//在pos位置插elem元素的拷贝,返回新数据的位置。

insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值。

insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值。

clear();//移除容器的所有数据

erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。

erase(pos);//删除pos位置的数据,返回下一个数据的位置。

remove(elem);//删除容器中所有与elem值匹配的元素。

 

 

7.3 list大小操作

size();//返回容器中元素的个数

empty();//判断容器是否为空

resize(num);//重新指定容器的长度为num,

若容器变长,则以默认值填充新位置。

如果容器变短,则末尾超出容器长度的元素被删除。

resize(num, elem);//重新指定容器的长度为num,

若容器变长,则以elem值填充新位置。

如果容器变短,则末尾超出容器长度的元素被删除。

 

7.4 list赋值操作

assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。

assign(n, elem);//将n个elem拷贝赋值给本身。

list& operator=(const list &lst);//重载等号操作符

swap(lst);//将lst与本身的元素互换。

7.5 list数据的存取

front();//返回第一个元素。

back();//返回最后一个元素。

7.6 list反转排序

reverse();//反转链表,比如lst包含1,3,5元素,运行此方法后,lst就包含5,3,1元素。

sort(); //list排序

 

八、set/multiset容器

Set的特性是。所有元素都会根据元素的键值自动被排序。Set的元素不像map那样可以同时拥有实值和键值,set的元素即是键值又是实值。Set不允许两个元素有相同的键值。

我们可以通过set的迭代器改变set元素的值吗?不行,因为set元素值就是其键值,关系到set元素的排序规则。如果任意改变set元素值,会严重破坏set组织。换句话说,set的iterator是一种const_iterator.

set拥有和list某些相同的性质,当对容器中的元素进行插入操作或者删除操作的时候,操作之前所有的迭代器,在操作完成之后依然有效,只有被删除的那个元素的迭代器才无效。

multiset特性及用法和set完全相同,唯一的差别在于它允许键值重复。set和multiset的底层实现是红黑树,红黑树为平衡二叉树的一种。

8.1 set构造函数

set<T> st;//set默认构造函数:

mulitset<T> mst; //multiset默认构造函数:

set(const set &st);//拷贝构造函数

8.2 set赋值操作

set& operator=(const set &st);//重载等号操作符

swap(st);//交换两个集合容器

8.3 set大小操作

size();//返回容器中元素的数目

empty();//判断容器是否为空

 

8.4 set插入和删除操作

insert(elem);//在容器中插入元素。

clear();//清除所有元素

erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器。

erase(beg, end);//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。

erase(elem);//删除容器中值为elem的元素。

 

8.5 set查找操作

find(key);//查找键key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();

count(key);//查找键key的元素个数

lower_bound(keyElem);//返回第一个key>=keyElem元素的迭代器。

upper_bound(keyElem);//返回第一个key>keyElem元素的迭代器。

equal_range(keyElem);//返回容器中key与keyElem相等的上下限的两个迭代器

8.6  对组(pair)

对组(pair)将一对值组合成一个值,这一对值可以具有不同的数据类型,两个值可以分别用pair的两个公有属性first和second访问。

//第一种方法创建一个对组
pair<string, int> pair1(string("name"), 20);
cout << pair1.first << endl; //访问pair第一个值
cout << pair1.second << endl;//访问pair第二个值

//第二种
pair<string, int> pair2 = make_pair("name", 30);
cout << pair2.first << endl;
cout << pair2.second << endl;

//pair=赋值
pair<string, int> pair3 = pair2;
cout << pair3.first << endl;
cout << pair3.second << endl;

 

九、map/multimap容器

Map的特性是,所有元素都会根据元素的键值自动排序。Map所有的元素都是pair,同时拥有实值和键值,pair的第一元素被视为键值,第二元素被视为实值,map不允许两个元素有相同的键值。

我们可以通过map的迭代器改变map的键值吗?答案是不行,因为map的键值关系到map元素的排列规则,任意改变map键值将会严重破坏map组织。如果想要修改元素的实值,那么是可以的。

Map和list拥有相同的某些性质,当对它的容器元素进行新增操作或者删除操作时,操作之前的所有迭代器,在操作完成之后依然有效,当然被删除的那个元素的迭代器必然是个例外。

Multimap和map的操作类似,唯一区别multimap键值可重复。

Map和multimap都是以红黑树为底层实现机制。

9.1 map构造函数

map<T1, T2> mapTT;//map默认构造函数:

map(const map &mp);//拷贝构造函数

 

9.2 map赋值操作

map& operator=(const map &mp);//重载等号操作符

swap(mp);//交换两个集合容器

 

9.3 map大小操作

size();//返回容器中元素的数目

empty();//判断容器是否为空

9.4 map插入数据元素操作

map.insert(...); //往容器插入元素,返回pair<iterator,bool>

map<int, string> mapStu;

// 第一种 通过pair的方式插入对象

mapStu.insert(pair<int, string>(3, "小张"));

// 第二种 通过pair的方式插入对象

mapStu.inset(make_pair(-1, "校长"));

// 第三种 通过value_type的方式插入对象

mapStu.insert(map<int, string>::value_type(1, "小李"));

// 第四种 通过数组的方式插入值

mapStu[3] = "小刘";

mapStu[5] = "小王";

9.5 map删除操作

clear();//删除所有元素

erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器。

erase(beg,end);//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。

erase(keyElem);//删除容器中key为keyElem的对组。

9.6 map查找操作

find(key);//查找键key是否存在,若存在,返回该键的元素的迭代器;/若不存在,返回map.end();

count(keyElem);//返回容器中key为keyElem的对组个数。对map来说,要么是0,要么是1。对multimap来说,值可能大于1。

lower_bound(keyElem);//返回第一个key>=keyElem元素的迭代器。

upper_bound(keyElem);//返回第一个key>keyElem元素的迭代器。

equal_range(keyElem);//返回容器中key与keyElem相等的上下限的两个迭代器。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章