STL容器


参考上位:

https://blog.csdn.net/u010183728/article/details/81913729#2.4.2%20queue%20%26%20priority_queue
https://www.cnblogs.com/yelongsan/p/4049698.html

顺序容器vector

连续存储结构,每个元素在内存上是连续的;支持 高效的随机访问和在尾端插入/删除操作,但其他位置的插入/删除操作效率低下;

#include < vector>

using namespace std;

构造

vector():创建一个空vector

vector(int nSize):创建一个vector,元素个数为nSize

vector(int nSize,const t& t):创建一个vector,元素个数为nSize,且值均为t

vector(const vector&):复制构造函数

vector(begin,end):复制[begin,end)区间内另一个数组的元素到vector中

增加

void push_back(const T& x):向量尾部增加一个元素X

iterator insert(iterator it,const T& x):向量中迭代器指向元素前增加一个元素x

iterator insert(iterator it,int n,const T& x):向量中迭代器指向元素前增加n个相同的元素x

iterator insert(iterator it,const_iterator first,const_iterator last):向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据

删除

iterator erase(iterator it):删除向量中迭代器指向元素

iterator erase(iterator first,iterator last):删除向量中[first,last)中元素

void pop_back():删除向量中最后一个元素

void clear():清空向量中所有元素

赋值

v.assign(n,elem) , 将n个elem的拷贝赋值给v。

v.assign(beg,end) , 将[beg; end)区间中的数据赋值给v。

引用

reference at(int pos):返回pos位置元素的引用

reference front():返回首元素的引用

reference back():返回尾元素的引用

遍历

iterator begin():返回向量头指针,指向第一个元素

iterator end():返回向量尾指针,指向向量最后一个元素的下一个位置

reverse_iterator rbegin():反向迭代器,指向最后一个元素

reverse_iterator rend():反向迭代器,指向第一个元素之前的位置

判断

bool empty() const:判断向量是否为空,若为空,则向量中无元素

大小

int size() const:返回向量中元素的个数

int capacity() const:返回当前向量所能容纳的最大元素值

int max_size() const:返回最大可允许的vector元素数量值

resize 改变当前使用数据的大小,如果它比当前使用的大,则填充默认值

reserve 改变当前vecotr所分配空间的大小即capacity的值

其他

v1.swap(v2):交换两个同类型向量的数据

访问方式

  • 数组[] 方式、

  • 迭代器

二维数组

vector< vector< int> >v;//这里最外的> >之间要有空格。否则在比较旧的编译器下无法通过

int N=5, M=6; 

vector<vector<int> > obj(N); //定义二维动态数组大小5行 

for(int i =0; i< obj.size(); i++)//动态二维数组为5行6列,值全为0 
{ 
	obj[i].resize(M); 
} 

 

for(int i=0; i< obj.size(); i++)//输出二维动态数组 
{
    for(int j=0;j<obj[i].size();j++)
    {
    	cout<<obj[i][j]<<" ";
    }
	cout<<"\n";
}
int N=5, M=6; 

vector<vector<int> > obj(N, vector<int>(M)); //定义二维动态数组5行6列 

for(int i=0; i< obj.size(); i++)//输出二维动态数组 
{
	for(int j=0;j<obj[i].size();j++)
	{
		cout<<obj[i][j]<<" ";
	}
	cout<<"\n";
}

return 0;

顺序容器deque

deque将内存分块,每次分配固定大小的分块,一个分块填充满后开辟新的分块,属于散布-连续混杂的情况

属于局部连续存储结构,类似于vector,不同之处在于, deque支持高效的首/尾端插入/删除操作。

所谓的deque是”double ended queue”的缩写,双端队列不论在尾部或头部插入元素,都十分迅速。而在中间插入元素则会比较费时,因为必须移动中间其他的元素。双端队列是一种随机访问的数据类型,提供了在序列两端快速插入和删除操作的功能,它可以在需要的时候改变自身大小,完成了标准的C++数据结构中队列的所有功能。

Vector是单向开口的连续线性空间,deque则是一种双向开口的连续线性空间。deque对象在队列的两端放置元素和删除元素是高效的,而向量vector只是在插入序列的末尾时操作才是高效的。

deque和vector的最大差异,

  • deque允许在常数时间内对头端进行元素的插入或移除操作
  • deque没有所谓的capacity观念,因为它是动态地以分段连续空间组合而成,随时可以增加一段新的空间并链接起来。换句话说,像vector那样“因旧空间不足而重新配置一块更大空间,然后复制元素,再释放旧空间”这样的事情在deque中是不会发生的。也因此,deque没有必要提供所谓的空间预留(reserved)功能。

虽然deque也提供Random Access Iterator,但它的迭代器并不是普通指针,其复杂度和vector不可同日而语,这当然涉及到各个运算层面。因此,除非必要,我们应尽可能选择使用vector而非deque。对deque进行的排序操作,为了最高效率,可将deque先完整复制到一个vector身上,将vector排序后(利用STL的sort算法),再复制回deque。

#include<deque>
using namespace std;

构造(同vector)

deque<type> deq; // 声明一个元素类型为type的双端队列que

deque<type> deq(size); // 声明一个类型为type、含有size个默认值初始化元素的的双端队列que

deque<type> deq(size, value); // 声明一个元素类型为type、含有size个value元素的双端队列que

deque<type> deq(mydeque); // deq是mydeque的一个副本

deque<type> deq(first, last); // 使用迭代器first、last范围内的元素初始化deq

引用

deq[ ]:用来访问双向队列中单个的元素。

deq.at(idx):传回索引idx所指的数据,如果idx越界,抛出out_of_range。

deq.front():返回第一个元素的引用。

deq.back():返回最后一个元素的引用。

赋值

deq.assign(beg,end):将[beg; end)区间中的数据赋值给deq。

deq.assign(n,elem):将n个elem的拷贝赋值给deq。

增加

c.insert(it,elem):在pos位置插入一个elem拷贝,传回新数据位置。

c.insert(it,n,elem):在pos位置插入>n个elem数据。无返回值。

c.insert(it,beg,end):在pos位置插入在[beg,end)区间的数据。无返回值。

deq.push_front(x):把元素x插入到双向队列的头部。

deq.push_back(x):把元素x插入到双向队列的尾部。

删除

c.erase(pos):删除pos位置的数据,传回下一个数据的位置。

c.erase(beg,end):删除[beg,end)区间的数据,传回下一个数据的位置。

deq.pop_front():弹出双向队列的第一个元素。

deq.pop_back():弹出双向队列的最后一个元素。

deq.clear();

判断

deq.empty()

大小

c.size():返回容器中实际数据的个数。

c.resize(num):重新指定队列的长度。

c.max_size():返回容器中最大数据的数量。

其他

c.get_allocator:使用构造函数返回一个拷贝

C1.swap(c2):将c1和c2元素互换。

deque的一些特点:

  1. 支持随机访问,即支持[ ]以及at(),但是性能没有vector好。
  2. 可以在内部进行插入和删除操作,但性能不及list。
  3. deque两端都能够快速插入和删除元素,而vector只能在尾端进行。
  4. deque的元素存取和迭代器操作会稍微慢一些,因为deque的内部结构会多一个间接过程。
  5. deque迭代器是特殊的智能指针,而不是一般指针,它需要在不同的区块之间跳转。
  6. deque可以包含更多的元素,其max_size可能更大,因为不止使用一块内存。
  7. deque不支持对容量和内存分配时机的控制。
  8. deque不提供容量操作:capacity()和reverse(),但是vector可以。
  9. 在除了首尾两端的其他地方插入和删除元素,都将会导致指向deque元素的任何pointers、references、iterators失效。不过,deque的内存重分配优于vector,因为其内部结构显示不需要复制所有元素。
  10. deque的内存区块不再被使用时,会被释放,deque的内存大小是可缩减的。不过,是不是这么做以及怎么做由实际操作版本定义。

顺序容器list

List是stl实现的双向链表,与向量(vectors)相比, 它允许快速的插入和删除,但是随机访问却比较慢。 list不支持下标访问和at访问

非连续存储结构,具有双链表结构,每个元素维护一对前向和后向指针,因此支持前向/后向遍历。 支持高效的随机插入/删除操作,但随机访问效率低下,且由于需要额外维护指针 ,开销也比较大

#include<list>
using namespace std;

构造(同vector)

list<int>lst1; //创建空list

list<int> lst2(5); //创建含有5个元素的list

list<int>lst3(3,2); //创建含有3个元素的list

list<int>lst4(lst2); //使用lst2初始化lst4

list<int>lst5(lst2.begin(),lst2.end()); //同lst4

引用

Lst1.front() 返回第一个元素

Lst1.back() 返回最后一个元素

遍历

Lst1.begin() 返回指向第一个元素的迭代器

Lst1.end() 返回末尾的迭代器

Lst1.rbegin() 返回指向第一个元素的逆向迭代器

Lst1.rend() 指向list末尾的逆向迭代器

增加

Lst1.push_front() 在list的头部添加一个元素

Lst1.push_back() 在list的末尾添加一个元素

Lst1.insert() 插入一个元素到list中

删除

Lst1.pop_front() 删除第一个元素

Lst1.pop_back() 删除最后一个元素

Lst1.erase() 删除一个元素

Lst1.clear() 删除所有元素

Lst1.unique() 删除list中相邻重复的元素

Lst1.remove(elem); remove和erase都是删除一个元素,其中remove参数类型和数据类型一致,而erase参数类型是迭代器。若有多个elem,都会删除

Lst1.remove_if(bool func(…)) 按指定条件删除元素 接受一个函数符作为参数,应用于list中的每个成员,如果返回true,则删除该元素

大小

Lst1.size() 返回list中的元素个数

Lst1.resize() 改变list的大小

Lst1.max_size() 返回list能容纳的最大元素数量

赋值

Lst1.assign(begin, end) 给list赋值

Lst1.assign(n, elem)

判断

Lst1.empty() 如果list是空的则返回true

splice()和merge()

Lst1.splice(iterator position, list& x) 合并两个list

list1.splice(it1, list2); //将list2中的所有元素拷贝到list1中。在list1中的起始位置是it1.复制结束后,list2将为空。

Lst1.splice(iterator position, list& x, iterator i) 合并两个list

list1.splice(it1, list2, it2); //这个功能是将list2中的元素,从it2开始,剪切到list1的it1起始的地方。

Lst1.splice(iterator position, list& x, iterator first, iterator last) 合并两个list

list1.splice(it1, list2, it2begin, it2end);

Lst1.merge() 合并两个list

list1.merge(list2); //注意:在使用merge之前,必须使list1和list2已经排好顺序。并且,合并之后list1仍然是有序的。

其他

Lst1.reverse(void) 把list的元素倒转

Lst1.sort(void) 给list排序

Lst1.swap(Lst2) 交换两个list

Lst1.get_allocator() 返回list的配置器

示例

list相关函数代码示例: https://www.cnblogs.com/nuosenli/articles/11362864.html

vector,list和deque区别

stl提供了三个最基本的容器:vector,list,deque。

  • vector拥有一段连续的内存空间,并且起始地址不变,因此它能非常好的支持随即存取,即[]操作符,但由于它的内存空间是连续的,所以在中间进行插入和删除会造成内存块的拷贝,另外,当该数组后的内存空间不够时,需要重新申请一块足够大的内存并进行内存的拷贝。这些都大大影响了vector的效率。
  • list就是数据结构中的双向链表,因此它的内存空间可以是不连续的,通过指针来进行数据的访问,这个特点使得它的随即存取变的非常没有效率,因此它没有提供[]操作符的重载。但由于链表的特点,它可以以很好的效率支持任意地方的删除和插入。
  • deque支持[]操作符,也就是支持随即存取,并且和vector的效率相差无几,它支持在两端的操作:push_back,push_front,pop_back,pop_front等,并且在两端操作上与list的效率也差不多。

因此在实际使用时,如何选择这三个容器中哪一个,应根据你的需要而定,一般应遵循下面的原则:

1、如果你需要高效的随即存取,而不在乎插入和删除的效率,使用vector

2、如果你需要大量的插入和删除,而不关心随即存取,则应使用list

3、如果你需要随即存取,而且关心两端数据的插入和删除,则应使用deque。

vector中的元素随机访问效率很高。

在vecotor中插入或者删除某个元素,需要将现有元素进行复制,移动。如果vector中存储的对象很大,或者构造函数复杂,则在对现有元素进行拷贝时开销较大,因为拷贝对象要调用拷贝构造函数。对于简单的小对象,vector的效率优于list。

关联容器map

#include<map>
using namespace std;

map的键值key不可重复,map支持[ ]运算符

map是字典的抽象数据结构。

map中的所有的元素都会根据key自动进行排序,而且所有的元素都是唯一的。map中的所有的元素都是pair,即键(key)和值(value)组成的序列(pair中的第一个元素为key,第二个元素为value)。

typedef std::map<int,string> EMPLOYEE_MAP;
EMPLOYEE_MAP employees;

employees.insert(EMPLOYEE_MAP::value_type(25301, "A"));
employees.insert(EMPLOYEE_MAP::value_type(25302, "B"));
employees.insert(EMPLOYEE_MAP::value_type(25303, "C"));
employees.insert(EMPLOYEE_MAP::value_type(25304, "D"));
employees.insert(EMPLOYEE_MAP::value_type(25305, "E"));

EMPLOYEE_MAP::iterator itEnd = employees.end();
for (EMPLOYEE_MAP::iterator it = employees.begin(); it != itEnd; ++it)
{
std::cout << it->first << "-";
       std::cout << it->second << endl;
}

输出结果
25301-A
25302-B
25303-C
25304-D
25305-E

声明

map<int, string> ID_Name;

基本操作函数

begin() 返回指向map头部的迭代器

end() 返回指向map末尾的迭代器

rbegin() 返回一个指向map尾部的逆向迭代器

rend() 返回一个指向map头部的逆向迭代器

insert() 插入元素

count() 返回指定元素出现的次数

erase() 删除一个元素

clear() 删除所有元素

empty() 如果map为空则返回true

find() 查找一个元素

size() 返回map中元素的个数

max_size() 返回可以容纳的最大元素个数

------------分割线-----------

key_comp() 返回比较元素key的函数

value_comp() 返回比较元素value的函数

lower_bound() 返回键值>=给定元素的第一个位置

upper_bound() 返回键值>给定元素的第一个位置

equal_range() 返回特殊条目的迭代器对

get_allocator() 返回map的配置器

swap() 交换两个map

迭代器

共有八个获取迭代器的函数:begin, end, rbegin,rend 以及对应的 cbegin, cend, crbegin,crend。

二者的区别在于,后者一定返回 const_iterator,而前者则根据map的类型返回iterator 或者 const_iterator。

插入操作

用insert插入pair数据

    map<int, string> mapStudent;  
    mapStudent.insert(pair<int, string>(1, "student_one"));  
    mapStudent.insert(pair<int, string>(2, "student_two"));  
    mapStudent.insert(pair<int, string>(3, "student_three"));  

    map<int, string>::iterator iter;  
    for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)  
       cout<<iter->first<<' '<<iter->second<<endl; 

用insert函数插入value_type数据

    map<int, string> mapStudent;    
    mapStudent.insert(map<int, string>::value_type (1, "student_one"));    
    mapStudent.insert(map<int, string>::value_type (2, "student_two"));    
    mapStudent.insert(map<int, string>::value_type (3, "student_three"));    
    map<int, string>::iterator iter;    
    for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)  
         cout<<iter->first<<' '<<iter->second<<endl;   

用insert函数进行多个插入

insert共有4个重载函数:

  • 插入单个键值对,并返回插入位置和成功标志,插入位置已经存在值时,插入失败
pair<iterator,bool> insert (const value_type& val);
  • 在指定位置插入,在不同位置插入效率是不一样的,因为涉及到重排
iterator insert (const_iterator position, const value_type& val);
  • 插入多个
void insert (InputIterator first, InputIterator last);
  • c++11开始支持,使用列表插入多个
void insert (initializer_list<value_type> il);

示例

#include <iostream>
#include <map>
 
int main()
{
    std::map<char, int> mymap;
 
    // 插入单个值
    mymap.insert(std::pair<char, int>('a', 100));
    mymap.insert(std::pair<char, int>('z', 200));
 
    //返回插入位置以及是否插入成功
    std::pair<std::map<char, int>::iterator, bool> ret;
    ret = mymap.insert(std::pair<char, int>('z', 500));
    if (ret.second == false) {
        std::cout << "element 'z' already existed";
        std::cout << " with a value of " << ret.first->second << '\n';
    }
 
    //指定位置插入
    std::map<char, int>::iterator it = mymap.begin();
    mymap.insert(it, std::pair<char, int>('b', 300));  //效率更高
    mymap.insert(it, std::pair<char, int>('c', 400));  //效率非最高
 
    //范围多值插入
    std::map<char, int> anothermap;
    anothermap.insert(mymap.begin(), mymap.find('c'));
 
    // 列表形式插入
    anothermap.insert({ { 'd', 100 }, {'e', 200} });
 
    return 0;
}
//第三种:用数组方式插入数据,下面举例说明  
  
#include <map>    
#include <string>    
#include <iostream>    
using namespace std;  
  
int main()    
{    
    map<int, string> mapStudent;    
    mapStudent[1] = "student_one";   
    mapStudent[2] = "student_two";    
    mapStudent[3] = "student_three";    
    map<int, string>::iterator iter;    
    for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)    
        cout<<iter->first<<' '<<iter->second<<endl;    
}

以上三种用法,虽然都可以实现数据的插入,但是它们是有区别的,当然了第一种和第二种在效果上是完成一样的。

用insert函数插入数据,在数据的插入上涉及到集合的唯一性这个概念,即当map中有这个关键字时,insert操作是插入数据不了的,但是用数组方式就不同了,它可以覆盖以前该关键字对应的值

用程序说明:

mapStudent.insert(map<int, string>::value_type (1, “student_one”));

mapStudent.insert(map<int, string>::value_type (1, “student_two”));

上面这两条语句执行后,map中1这个关键字对应的值是“student_one”,第二条语句并没有生效,那么这就涉及到我们怎么知道insert语句是否插入成功的问题了,可以用pair来获得是否插入成功,

程序如下

pair<map<int, string>::iterator, bool> Insert_Pair;

Insert_Pair = mapStudent.insert(map<int, string>::value_type (1, “student_one”));

我们通过pair的第二个变量来知道是否插入成功,它的第一个变量返回的是一个map的迭代器,如果插入成功的话Insert_Pair.second应该是true的,否则为false。

查找

iterator find (const key_type& k);

const_iterator find (const key_type& k) const;

删除

//删除迭代器指向位置的键值对,并返回一个指向下一元素的迭代器
iterator erase( iterator pos ) 

//删除一定范围内的元素,并返回一个指向下一元素的迭代器
iterator erase( const_iterator first, const_iterator last );

//根据Key来进行删除, 返回删除的元素数量,在map里结果非0即1
size_t erase( const key_type& key );

//清空map,清空后的size为0
void clear();

交换

//就是两个map的内容互换
void swap( map& other );

容量

//查询
bool empty();

//查询map中键值对的数量
size_t size();

//查询map所能包含的最大键值对数量,和系统和应用库有关。
//注意:这并不意味着用户一定可以存这么多,很可能还没达到就已经开辟内存失败了
size_t max_size();

//查询关键字为key的元素的个数,在map里结果非0即1
size_t count( const Key& key ) const; 


扩展:unordered_map

在c++11标准前,c++标准库中只有一种map,但是它的底层实现并不是适合所有的场景,如果我们需要其他适合的map实现就不得不使用比如boost库等三方的实现,在c++11中加了一种map,即: unordered_map,unordered_set,他们的实现有什么不同呢?

  • unordered_map的底层采用哈希表的实现,查询的时间复杂度为是O(1)。所以unordered_map内部就是无序的,数据是按散列函数插入到槽里面去的,数据之间无顺序可言,如果我们不需要内部有序,就可以使用unordered_map。unordered_map也是属于关联式容器,采用std::pair保存key-value形式的数据。用法与map一致。

  • map底层采用的是红黑树的实现查询的时间复杂度为O(logn),看起来并没有unordered_map快,但是也要看实际的数据量,虽然unordered_map的查询从算法上分析比map快,但是它有一些其它的消耗,比如哈希函数的构造和分析,还有如果出现哈希冲突解决哈希冲突等等都有一定的消耗,因此unordered_map的效率在很大的程度上由它的hash函数算法决定,而红黑树的效率是一个稳定值。

unordered_map用hash结构会有一定的内存损失,它的内存占用高于map,但是效率会更高。

关于自定义类型的重载

STL中的map因为是有序的二叉树存储,所以对key值需要有大小的判断,当使用内置类型时,无需重载operator < ;但是用用户自定义类型的话,就需要重载operator < 。 unoredered_map全程使用不需要比较元素的key值的大小,但是,对于元素的==要有判断,又因为需要使用hash映射,所以,对于非内部类型,需要程序员为其定义这二者的内容,对于内部类型,就不需要了。

unordered库使用“桶”来存储元素,散列值相同的被存储在一个桶里。当散列容器中有大量数据时,同一个桶里的数据也会增多,造成访问冲突,降低性能。为了提高散列容器的性能,unordered库会在插入元素时自动增加桶的数量,不需要用户指定。但是,用户也可以在构造函数或者rehash()函数中,指定最小的桶的数量。个人理解: 此处说的“散列值相同的被存储在一个桶里”, 是不是指unordered_multimap, 因为unordered_map的key不可重复,个人猜测此处的“散列值相同”指的是key。

总结: 首先如果你需要对map中的数据排序,就首选map,他会把你的数据按照key的自然排序排序(由于它的底层实现红黑树机制所以会排序),如果不需要排序,就看你对内存和效率的选择了,unordered_map会比map多占用内存,但是效率会更高。

至于使用方法和函数,两者差不多。unordered_multimap用法亦可类推。

关联容器multimap

#include<map>
using namespace std;

multimap的键值key可以重复

也正是由于这种区别,map支持[ ]运算符,multimap不支持[ ]运算符。在用法上没什么区别。

关联容器set

#include<set>
using namespace std;

set的所有的元素都是有序的而且set中所有的元素都是唯一的;

set中的元素即是键值又是实值

set<string> fruits;
fruits.insert("apple");
fruits.insert("orange");
fruits.insert("banana");
set<string>::iterator itEnd = fruits.end();
for (set<string>::iterator it=fruits.begin(); it != itEnd; it++)
cout << *it << " ";

输出结果
         apple banana orange

不能通过set的迭代器去修改set元素,原因是修改元素会破坏set组织。

当对容器中的元素进行插入或者删除时,操作之前的所有迭代器在操作之后依然有效。

由于set元素是排好序的,且默认为升序,因此当set集合中的元素为结构体或自定义类时,该结构体或自定义类必须实现运算符<的重载

常用成员函数

begin()–返回指向第一个元素的迭代器
end()–返回指向最后一个元素的迭代器
rbegin()–返回指向集合中最后一个元素的反向迭代器
rend()–返回指向集合中第一个元素的反向迭代器
insert()–在集合中插入元素
find()–返回一个指向被查找到元素的迭代器
erase()–删除集合中的元素
clear()–清除所有元素
count()–返回某个值元素的个数
empty()–如果集合为空,返回true
max_size()–返回集合能容纳的元素的最大限值
size()–集合中元素的数目
swap()–交换两个集合变量
get_allocator()–返回集合的分配器
lower_bound()–返回指向大于(或等于)某值的第一个元素的迭代器
upper_bound()–返回大于某个值元素的迭代器
key_comp()–返回一个用于元素间值比较的函数
value_comp()–返回一个用于比较元素间的值的函数
equal_range()–返回集合中与给定值相等的上下限的两个迭代器

示例

set容器中,元素类型为基本类型,如何让set按照用户意愿来排序?

set容器中,如何让元素类型为自定义类型? 注意此处为什么没有重载 < , 是因为使用了仿函数

set容器的insert函数的返回值为什么类型?

#include <iostream>
#include <string>
#include <set>
using namespace std;
 
void test01()
{
    //set容器默认从小到大排序
    set<int> s;
    s.insert(10);
    s.insert(20);
    s.insert(30);
 
    //输出set
    PrintSet(s);
    //结果为:10 20 30
 
    /* set的insert函数返回值为一个对组(pair)。
       对组的第一个值first为set类型的迭代器:
       1、若插入成功,迭代器指向该元素。
       2、若插入失败,迭代器指向之前已经存在的元素
       对组的第二个值seconde为bool类型:
       1、若插入成功,bool值为true
       2、若插入失败,bool值为false
    */
    pair<set<int>::iterator, bool> ret = s.insert(40);
    if (true == ret.second)
        cout << *ret.first << " 插入成功" << endl;
    else
        cout << *ret.first << " 插入失败" << endl;
}
 
void test02()
{
    /* 如果想让set容器从大到小排序,需要给set容
       器提供一个仿函数,本例的仿函数为CompareSet
    */
    set<int, CompareSet> s;
    s.insert(10);
    s.insert(20);
    s.insert(30);
    
    //打印set
    PrintSet(s);
    //结果为:30,20,10
}
 
void test03()
{
    /* set元素类型为Person,当set元素类型为自定义类型的时候
       必须给set提供一个仿函数,用于比较自定义类型的大小,
       否则无法通过编译 
    */
    set<Person,ComparePerson> s;
    s.insert(Person("John", 22));
    s.insert(Person("Peter", 25));
    s.insert(Person("Marry", 18));
    s.insert(Person("Peter", 36));
 
    //打印set
    PrintSet(s);
}
 
int main(void)
{
    //test01();
    //test02();
    //test03();
    return 0;
}
/// 
/// 
/// 
/* 仿函数CompareSet,在test02使用 */
class CompareSet
{
public:
    //从大到小排序
    bool operator()(int v1, int v2)
    {
        return v1 > v2;
    }
    //从小到大排序
    //bool operator()(int v1, int v2)
    //{
    //    return v1 < v2;
    //}
};
/// 
/// 
/// 
/* Person类,用于test03 */
class Person
{
    friend ostream &operator<<(ostream &out, const Person &person);
public:
    Person(string name, int age)
    {
        mName = name;
        mAge = age;
    }
public:
    string mName;
    int mAge;
};
 
ostream &operator<<(ostream &out, const Person &person)
{
    out << "name:" << person.mName << " age:" << person.mAge << endl;
    return out;
}
/// 
/// 
///  
/* 仿函数ComparePerson,用于test03 */
class ComparePerson
{
public:
    //名字大的在前面,如果名字相同,年龄大的排前面
    bool operator()(const Person &p1, const Person &p2)
    {
        if (p1.mName == p2.mName)
        {
            return p1.mAge > p2.mAge;
        }
        return p1.mName > p2.mName;
    }
};
/// 
/// 
///  
/* 打印set类型的函数模板 */
template<typename T>
void PrintSet(T &s)
{
    for (T::iterator iter = s.begin(); iter != s.end(); ++iter)
        cout << *iter << " ";
    cout << endl;
}

关联容器multiset

#include<set>
using namespace std;

multiset和set基本相同,所不同的是,一个multiset中的元素是可以重复的。

set和multiset的底层实现是一种高效的平衡二叉树,即红黑树(Red-Black Tree)。

multiset容器的insert函数返回值为什么?

multiset的insert函数返回值为multiset类型的迭代器,

指向新插入的元素。multiset允许插入相同的值,因此

插入一定成功,因此不需要返回bool类型。

容器共性

容器都是物之所在,这就决定了它们必然存在很多共性,这些共性包括迭代器、大小等属性。容器与容器之间的主要区别体现在对数据的操作上。

每类容器都包含四个迭代器:iterator(正向迭代器)、const_iterator(常正向迭代器)、reverse_iterator(反向迭代器)、const_reverse_iterator(常反向迭代器)。因此你可以按照下面的方式获取每个容器的相应的迭代器:

获取正向迭代器

C<T>::iterator it = c.begin();

C <T>::iterator it = c.end();

获取反向迭代器

C <T>::reverse_iterator it = c.rbegin();

C <T>:: reverse_iterator it = c.rend()

获取常正向迭代器

C<T>::const_iterator it = c.begin();

C <T>:: const_iterator it = c.end();

获取常反向迭代器

C <T>:: const_ reverse_iterator it = c.rbegin();

C <T>:: const_ reverse_iterator it = c.rend()

注:C为容器类型,c为C的实例

所有容器是数据的存在之处,可以看作的是数据的集合,因此它们都会有大小、是否为空等属性,因此你可以按照下面的方式获取所有的容器的公共属性:

获取容器的大小

c.size();

判断容器是否为空

c.empty();

顺序容器共性

c.push_back

c.pop_back

c.push_front

c.pop_front

c.back

c.front

c.erase

c.remove

capacity、max_size属性

  • capacity属性,表示STL在发生realloc前能允许的最大元素数,也可以理解为预分配的内存空间。例如一个vector<int> v的capacity为5,当插入第6个元素时,vector会realloc,vector内部数据会复制到另外一个内存区域。这样之前指向vector中的元素的指针、迭代器等等均会失效。

  • max_size属性和capacity不同,表示STL容器允许的最大元素数,通常,这个数是一个很大的常整数,可以理解为无穷大。这个数目与平台和实现有关

并不是所有的容器都会发生realloc,List,Map/Multimap,Set/Multiset的元素在内存中散布,不预分配内存,所以不会产生realloc的情况,对于这些容器,其capacity是无意义的,所以这些容器没有capacity()成员函数,也没有capacity属性。

deque双向队列情况比较特殊,deque将内存分块,每次分配固定大小的分块,一个分块填充满后开辟新的分块,也属于散布-连续混杂的情况,虽然deque会预分配内存空间,但也不会产生realloc(人家是alloc),所以也不具有capacity属性。

实际具有capacity属性的容器只有vector、string、 basic_string

  • vector<T>

默认构造函数 : capacity = 0

使用构造函数vector(n, value=T()) ,capacity = n

  • string (basic_string )

默认构造函数 :capacity = 15

没有指定capacity的构造式

指定了初始字符串的:capacity = 大于字符串长度且等于n*15-1

  • basic_string<wchar_t>

默认构造函数:capacity = 7

指定了初始字符串的: capacity = 大于字符串长度且等于n*8-1

除了通过构造式设定capacity,也可以使用reserve(n)来设定容器对象的capacity,避免之后的realloc。不过这一过程是只增不减的,如果n小于当前capacity,则reserve(n)无效。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值