STL标准模板库

STL(Standard Template Library,标准模板库)是惠普实验室开发的一系列软件的统称。现然主要出现在C++中,但在被引入C++之前该技术就已经存在了很长的一段时间。

STL的从广义上讲分为三类:algorithm(算法)、container(容器)和iterator(迭代器),容器和算法通过迭代器可以进行无缝地连接。几乎所有的代码都采 用了模板类和模板函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。在C++标准中,STL被组织为下面的13个头文 件:<algorithm>、<deque>、<functional>、<iterator>、<vector>、<list>、<map>、<memory>、<numeric>、<queue>、<set>、<stack> 和<utility>。



迭代器      

  迭代器从作用上来说是最基本的部分,可是理解起来比前两者都要费力一些。软件设计有一个基本原则,所有的问题都可以通过引进一个间接层来简化, 这种简化在STL中就是用迭代器来完成的。概括来说,迭代器在STL中用来将算法和容器联系起来,起着一种黏和剂的作用。几乎STL提供的所有算法都是通 过迭代器存取元素序列进行工作的,每一个容器都定义了其本身所专有的迭代器,用以存取容器中的元素。

  迭代器部分主要由头文件<utility>,<iterator>和<memory>组 成。<utility>是一个很小的头文件,它包括了贯穿使用在STL中的几个模板的声明,<iterator>中提供了迭代器 使用的许多方法,而对于<memory>的描述则十分的困难,它以不同寻常的方式为容器中的元素分配存储空间,同时也为某些算法执行期间产生 的临时对象提供机制,<memory>中的主要部分是模板类allocator,它负责产生所有容器中的默认分配器。

算法

      函数库对数据类型的选择对其可重用性起着至关重要的作用。举例来说,一个求方根的函数,在使用浮点数作为其参数类型的情况下的可重用性肯定比使 用整型作为它的参数类性要高。而C++通过模板的机制允许推迟对某些类型的选择,直到真正想使用模板或者说对模板进行特化的时候,STL就利用了这一点提 供了相当多的有用算法。它是在一个有效的框架中完成这些算法的——可以将所有的类型划分为少数的几类,然后就可以在模版的参数中使用一种类型替换掉同一种 类中的其他类型。
  STL提供了大约100个实现算法的模版函数,比如算法for_each将为指定序列中的每一个元素调用指定的函数,stable_sort以 你所指定的规则对序列进行稳定性排序等等。这样一来,只要熟悉了STL之后,许多代码可以被大大的化简,只需要通过调用一两个算法模板,就可以完成所需要 的功能并大大地提升效率。

        算法部分主要由头文件<algorithm>,<numeric>和<functional>组 成。<algorithm>是所有STL头文件中最大的一个(尽管它很好理解),它是由一大堆模版函数组成的,可以认为每个函数在很大程度上 都是独立的,其中常用到的功能范围涉及到比较、交换、查找、遍历操作、复制、修改、移除、反转、排序、合并等等。<numeric>体积很 小,只包括几个在序列上面进行简单数学运算的模板函数,包括加法和乘法在序列上的一些操作。<functional>中则定义了一些模板类, 用以声明函数对象。

使用STL的好处:

1)STL是C++的一部分,因此不用额外安装什么,它被内建在你的编译器之内。
2)STL的一个重要特点是数据结构和算法的分离。尽管这是个简单的概念,但是这种分离确实使得STL变得非常通用。
例如,在STL的vector容器中,可以放入元素、基础数据类型变量、元素的地址;
STL的sort()函数可以用来操作vector,list等容器。
3) 程序员可以不用思考STL具体的实现过程,只要能够熟练使用STL就OK了。这样他们就可以把精力放在程序开发的别的方面。
4)   STL具有高可重用性,高性能,高移植性,跨平台的优点。
      高可重用性:STL中几乎所有的代码都采用了模板类和模版函数的方式实现,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。关于模板的知识,已经给大家介绍了。
      高性能:如map可以高效地从十万条记录里面查找出指定的记录,因为map是采用红黑树的变体实现的。(红黑树是平横二叉树的一种)
      高移植性:如在项目A上用STL编写的模块,可以直接移植到项目B上。
               跨平台:如用windows的Visual Studio编写的代码可以在Mac OS的XCode上直接编译。
5) 程序员可以不用思考STL具体的实现过程,只要能够熟练使用STL就OK了。这样他们就可以把精力放在程序开发的别的方面。
6) 了解到STL的这些好处,我们知道STL无疑是最值得C++程序员骄傲的一部分。每一个C++程序员都应该好好学习STL。只有能够熟练使用STL的程序员,才是好的C++程序员。
7) 总之:招聘工作中,经常遇到C++程序员对STL不是非常了解。大多是有一个大致的映像,而对于在什么情况下应该使用哪个容器和算法都感到比较茫然。STL是C++程序员的一项不可或缺的基本技能,掌握它对提升C++编程大有裨益。
 

容器

        在实际的开发过程中,数据结构本身的重要性不会逊于操作于数据结构的算法的重要性,当程序中存在着对时间要求很高的部分时,数据结构的选择就显得更加重要。
  经典的数据结构数量有限,但是我们常常重复着一些为了实现向量、链表等结构而编写的代码,这些代码都十分相似,只是为了适应不同数据的变化而在 细节上有所出入。STL容器就为我们提供了这样的方便,它允许我们重复利用已有的实现构造自己的特定类型下的数据结构,通过设置一些模板,STL容器对最常用的数据结构提供了支持,这些模板的参数允许我们指定容器中元素的数据类型,可以将我们许多重复而乏味的工作简化。

    容器部分主要由头文 件<vector>,<list>,<deque>,<set>,<map>,<stack> 和<queue>组成。对于常用的一些容器和容器适配器(可以看作由其它容器实现的容器),可以通过下表总结一下它们和相应头文件的对应关系。

下面对于常见的容器进行具体介绍:

1,string

string是STL的字符串类型,通常用来表示字符串。而在使用string之前,字符串通常是用char*表示的。string与char*都可以用来表示字符串,那么二者有什么区别呢。
string和char*的比较
string是一个类, char*是一个指向字符的指针。
         string封装了char*,管理这个字符串,是一个char*型的容器。
string不用考虑内存释放和越界。
         string管理char*所分配的内存。每一次string的复制,取值都由string类负责维护,不用担心复制越界和取值越界等。
string提供了一系列的字符串操作函数(这个等下会详讲)

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

string的构造函数:

默认构造函数:
             string();    //构造一个空的字符串string s1。
拷贝构造函数:
             string(const string &str);     //构造一个与str一样的string。如string s1(s2)。
带参数的构造函数
            string(const char *s);    //  用字符串s初始化

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

string类的字符操作:
const char &operator[] (int n) const;
const char &at(int n) const;
char &operator[] (int n);
char &at(int n);
operator[]和at()均返回当前字符串中第n个字符,但二者是有区别的。

        主要区别在于at()在越界时会抛出异常,[]在刚好越界时会返回(char)0,再继续越界时,编译器直接出错。如果你的程序希望可以通过try,catch捕获异常,建议采用at()。

int copy(char *s, int n, int pos=0) const;  

      把当前串中以pos开始的n个字符拷贝到以s为起始位置的字符数组中,返回实际拷贝的数目。注意要保证s所指向的空间足够大以容纳当前字符串,不然会越界。

int length() const;         //返回当前字符串的长度。长度不包括字符串结尾的'\0'。
bool empty() const;     //当前字符串是否为空
 string &operator=(const string &s);     //把字符串s赋给当前的字符串
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个字符赋给当前字符串
 string &operator+=(const string &s);  //把字符串s连接到当前字符串结尾
string &operator+=(const char *s);//把字符串s连接到当前字符串结尾
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

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

compare函数在>时返回 1,<时返回 -1,==时返回 0。比较区分大小写,比较时参考字典顺序,排越前面的越小。大写的A比小写的a小。

查找
int find(char c,int pos=0) const;  //从pos开始查找字符c在当前字符串的位置 
int find(const char *s, int pos=0) const;  //从pos开始查找字符串s在当前字符串的位置
int find(const string &s, int pos=0) const;  //从pos开始查找字符串s在当前字符串中的位置
find函数如果查找不到,就返回-1
int rfind(char c, int pos=npos) const;   //从pos开始从后向前查找字符c在当前字符串中的位置 
int rfind(const char *s, int pos=npos) const;
int rfind(const string &s, int pos=npos) const;
//rfind是反向查找的意思,如果查找不到, 返回-1

替换
string &replace(int pos, int n, const char *s);//删除从pos开始的n个字符,然后在pos处插入串s
string &replace(int pos, int n, const string &s);  //删除从pos开始的n个字符,然后在pos处插入串s

void swap(string &s2);    //交换当前字符串与s2的值

string &insert(int pos, const char *s);
string &insert(int pos, const string &s);
//前两个函数在pos位置插入字符串s
string &insert(int pos, int n, char c);  //在pos位置 插入n个字符c

string &erase(int pos=0, int n=npos);  //删除pos开始的n个字符,返回修改后的字符串

#include <iostream>
#include <string>
#include <exception>
#include <cstring>

using namespace std;

void StringInit()
{
	string s1;    
	string s2("helloworld");
	string s3(s2);     
	string s4(10, 'a');

	cout << s1 << endl;
	cout << s2 << endl;
	cout << s3 << endl;
	cout << s4 << endl;
}

void StringAt()
{
	string s1("helloworld");

	cout << s1[0] << endl;
	cout << s1.at(2) << endl;


	try
	{
		cout << s1.at(12) << endl;    
	}
	catch (exception &e)
	{
		cout << "catch a exception : " << e.what() << endl;
	}
}

void StringStr()
{
	string s("helloworld");
	const char *str;

	str = s.c_str();
	cout << str << endl;
}

void StringCopy()
{
	string s("helloworld");
	char buf[32] = {0};

	s.copy(buf, 5, 5);
	cout << buf << endl;
}

void StringLength()
{
	string s("helloworld");

	cout << "length : " << s.length() << endl;

	if (s.empty())
	{
		cout << "null" << endl;
	}
	else
	{
		cout << "not null" << endl;
	}
}

void StringAssign()
{
	string s1("helloworld");
	string s2("test");

	s2 = s1;
	cout << s2 << endl;

	s2.assign("test");
	cout << s2 << endl;
	s2.assign("helloworld", 5);
	cout << s2 << endl;
	s2.assign(s1);
	cout << s2 << endl;
}

void StringAppend()
{
	string s1("helloworld");
	string s2("01234567");

	s1 = s1 + s2;
	cout << s1 << endl;

	s1.append(s2);
	cout << s1 << endl;

	s1.append(s2, 3, 2);
	cout << s1 << endl;

	s1.append(5, 'a');
	cout << s1 << endl;
}

void StringCompare()
{
	string s1("hello");
	string s2("world");

	if (1 == s1.compare(s2))
	{
		cout << "s1 > s2" << endl; 
	}
	else if (-1 == s1.compare(s2))
	{
		cout << "s1 < s2" << endl;
	}
	else
	{
		cout << "s1 = s2" << endl;
	}
}

void StringSub()
{
	string s1("helloworld");

	cout << s1.substr(5, 5) << endl;
}

void StringFind()
{
	string s1("helloworldhelloworldhelloworld");

	int index = s1.find('x', 1);
	cout << index << endl;

	index = s1.find("world", 6);
	cout << index << endl;

	//s1.replace(5, 2, "xxxxxx");
	//cout << s1 << endl;

	index = s1.find("hello", 0);
	while (index != -1)
	{
		s1.replace(index, 5, "xx");
		index = s1.find("hello", index + strlen("xx"));
	}
	cout << s1 << endl;
}

void StringInsert()
{
	string s("helloworld");

	s.insert(5, "1234");
	cout << s << endl;

	s.erase(5, 4);
	cout << s << endl;
}


int main()
{
	StringInit();
	StringAt();
	StringStr();
	StringCopy();
	StringLength();
	StringAssign();
	StringAppend();
	StringCompare();
	StringSub();
	StringFind();
	StringInsert();

	return 0;
}

2,vector

vector是将元素置于一个动态数组中加以管理的容器。
vector可以随机存取元素(支持索引值直接存取, 用[]操作符或at()方法)。

vector尾部添加或移除元素非常快速。但是在中部或头部插入元素或移除元素比较费时

理论知识
vector.size();     //返回容器中元素的个数
vector.empty();    //判断容器是否为空
vector.resize(num);   //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
vector.resize(num, elem);  //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。

理论知识
vector.insert(pos,elem);   //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
vector.insert(pos,n,elem);   //在pos位置插入n个elem数据,无返回值。
vector.insert(pos,beg,end);   //在pos位置插入[beg,end)区间的数据,无返回值 

vector.clear();    //移除容器的所有数据
vec.erase(beg,end);  //删除[beg,end)区间的数据,返回下一个数据的位置。
vec.erase(pos);    //删除pos位置的数据,返回下一个数据的位置。
简单案例:
删除区间内的元素
vecInt是用vector<int>声明的容器,现已包含按顺序的1,3,5,6,9元素。
vector<int>::iterator itBegin=vecInt.begin()+1;
vector<int>::iterator itEnd=vecInt.begin()+2;
vecInt.erase(itBegin,itEnd);
//此时容器vecInt包含按顺序的1,6,9三个元素。

假设 vecInt 包含1,3,2,3,3,3,4,3,5,3,删除容器中等于3的元素
for(vector<int>::iterator it=vecInt.being(); it!=vecInt.end(); )    //小括号里不需写  ++it
{
   if(*it == 3)
   {
        it  =  vecInt.erase(it);       //以迭代器为参数,删除元素3,并把数据删除后的下一个元素位置返回给迭代器。
         //此时,不执行  ++it;  
   }
   else
   {
       ++it;
   }
}

//删除vecInt的所有元素

vecInt.clear();            //容器为空

这里对vector容器进行了详细的介绍,后面的部分容器的操作和vector相似,就不一一做详细解释。但其他容器如果有不同之处,我会做对比。

#include <iostream>
#include <vector>

using namespace std;

void VectorInit()
{
	int array[10] = {1, 2, 3, 4, 5, 6, 7};
	vector<int> v1;    
	vector<int> v2(array, array + 4);
	vector<int> v3(v2);   
	vector<int> v4(10, 1);
	
	for (int i = 0; i < v2.size(); i++)
	{
		cout << v2[i] << endl;
	}

	for (int i = 0; i < v3.size(); i++)
	{
		cout << v3[i] << endl;
	}

	for (int i = 0; i < v4.size(); i++)
	{
		cout << v4[i] << endl;
	}
}

void VectorAssign()
{
	int array[10] = {1, 2, 3, 4, 5, 6, 7};
	vector<int> v1;

	v1.assign(array, array + 3);
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}
	cout << endl;

	v1.assign(3, 9);
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}
	cout << endl;

}

void VectorSwap()
{
	vector<int> v1(10), v2(5);

	for (int i = 0; i < 10; i++)
	{
		v1[i] = i + 1;
	}

	for (int i = 0; i < 5; i++)
	{
		v2[i] = i + 10;
	}

	v1.swap(v2);

	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}
	cout << endl;
}

void VectorSize()
{
	vector<int> v1(10);

	if (v1.empty())
	{
		cout << "vector is null" << endl;
	}
	else
	{
		cout << "vector is not null" << endl;
	}

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

	for (int i = 0; i < v1.size(); i++)
	{
		v1[i] = i + 1;
	}

	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i]  << " ";
	}
	cout << endl;

	v1.resize(20, 1);
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i]  << " ";
	}
	cout << endl;

	v1.resize(5);
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i]  << " ";
	}
	cout << endl;
}

void VectorAt()
{
	vector<int> v1(10);

	for (int i = 0; i < v1.size(); i++)
	{
		v1.at(i) = i + 1;
	}

	v1.front() = 100;
	v1.back() = 200;

	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i]  << " ";
	}
	cout << endl;

}

void VectorIterator()
{
	vector<int> v(10);

	for (int i = 0; i < v.size(); i++)
	{
		v[i] = i + 1;
	}

	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;

	for (vector<int>::reverse_iterator rit = v.rbegin(); rit != v.rend(); rit++)
	{
		cout << *rit << " ";
	}
	cout << endl;
}

void VectorInsert()
{
	vector<int> v1, v2;

	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i + 1);
		v2.push_back(i + 10);
	}

	for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;

	for (vector<int>::iterator it = v2.begin(); it != v2.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;

	v1.insert(v1.begin(), 100);
	for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;

	v1.insert(v1.begin(), v2.begin(), v2.end());
	for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;

	v1.erase(v1.begin());
	for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;

	v1.erase(v1.begin(), v1.begin() + 5);
	for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;

	v1.clear();
	for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;

}

int main()
{
	VectorInit();
	VectorAssign();
	VectorSwap();
	VectorSize();
	VectorAt();
	VectorIterator();
	VectorInsert();

	return 0;
}

3,list

list是一个双向链表容器,可高效地进行插入删除元素。
list不可以随机存取元素,所以不支持at.(pos)函数与[]操作符。It++(ok) it+5(err)

#include <list>  

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

4,deque

deque是“double-ended queue”的缩写,和vector一样都是STL的容器,deque是双端数组,而vector是单端的。
deque在接口上和vector非常相似,在许多操作的地方可以直接替换。
deque可以随机存取元素(支持索引值直接存取, 用[]操作符或at()方法)。
deque头部和尾部添加或移除元素都非常快速。但是在中部安插元素或移除元素比较费时。

#include <deque>  

理论知识:
deque.push_back(elem); //在容器尾部添加一个数据
deque.push_front(elem); //在容器头部插入一个数据
deque.pop_back();             //删除容器最后一个数据
deque.pop_front();       //删除容器第一个数据

5,stack

stack是堆栈容器,是一种“先进后出”的容器。
stack是简单地装饰deque容器而成为另外的一种容器。
#include <stack>  

6,queue

queue是队列容器,是一种“先进先出”的容器。
queue是简单地装饰deque容器而成为另外的一种容器。
#include <queue>  

7,set/multiset

set是一个集合容器,其中所包含的元素是唯一的,集合中的元素按一定的顺序排列。元素插入过程是按排序规则插入,所以不能指定插入位置。
set采用红黑树变体的数据结构实现,红黑树属于平衡二叉树。在插入操作和删除操作上比vector快。
set不可以直接存取元素。(不可以使用at.(pos)与[]操作符)。
multiset与set的区别:set支持唯一键值,每个元素值只能出现一次;而multiset中同一值可以出现多次。
不可以直接修改set或multiset容器中的元素值,因为该类容器是自动排序的。如果希望修改一个元素值,必须先删除原有的元素,再插入新的元素。

#include <set>  

set.find(elem);   //查找elem元素,返回指向elem元素的迭代器。
set.count(elem);   //返回容器中值为elem的元素个数。对set来说,要么是0,要么是1。对multiset来说,值可能大于1。
set.lower_bound(elem);  //返回第一个>=elem元素的迭代器。
set.upper_bound(elem);     //  返回第一个>elem元素的迭代器。

set.equal_range(elem);         //返回容器中与elem相等的上下限的两个迭代器。上限是闭区间,下限是开区间,如[beg,end)。

set<int,less<int> >  setIntA;  //该容器是按升序方式排列元素。
set<int,greater<int>> setIntB;   //该容器是按降序方式排列元素。
set<int> 相当于 set<int,less<int>>。
less<int>与greater<int>中的int可以改成其它类型,该类型主要要跟set容纳的数据类型一致。

以上函数返回两个迭代器,而这两个迭代器被封装在pair中。

以下讲解pair的含义与使用方法。

pair译为对组,可以将两个值视为一个单元。
pair<T1,T2>存放的两个值的类型,可以不一样,如T1为int,T2为float。T1,T2也可以是自定义类型。
pair.first是pair里面的第一个值,是T1类型。
pair.second是pair里面的第二个值,是T2类型。

8,map/multimap

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

#include <map> 

 理论提高:所有容器提供的都是值(value)语意,而非引用(reference)语意。容器执行插入元素的操作时,内部实施拷贝动作。所以STL容器内存储的元素必须能够被拷贝(必须提供拷贝构造函数)。
 
除了queue与stack外,每个容器都提供可返回迭代器的函数,运用返回的迭代器就可以访问元素。
通常STL不会丢出异常。要求使用者确保传入正确的参数。
每个容器都提供了一个默认构造函数跟一个默认拷贝构造函数。
如已有容器vecIntA。 
vector<int> vecIntB(vecIntA);  //调用拷贝构造函数,复制vecIntA到vecIntB中。
与大小相关的操作方法(c代表容器):
c.size();   //返回容器中元素的个数
c.empty();   //判断容器是否为空
比较操作(c1,c2代表容器):
c1 == c2     判断c1是否等于c2
c1 != c2      判断c1是否不等于c2

c1 = c2        把c2的所有元素指派给c1

各容器的使用场景:

 deque的使用场景:比如排队购票系统,对排队者的存储可以采用deque,支持头端的快速移除,尾端的快速添加。如果采用vector,则头端移除时,会移动大量的数据,速度慢。
vector与deque的比较:
一:vector.at()比deque.at()效率高,比如vector.at(0)是固定的,deque的开始位置却是不固定的。
二:如果有大量释放操作的话,vector花的时间更少,这跟二者的内部实现有关。
三:deque支持头部的快速插入与快速移除,这是deque的优点。
list的使用场景:比如公交车乘客的存储,随时可能有乘客下车,支持频繁的不确实位置元素的移除插入。
set的使用场景:比如对手机游戏的个人得分记录的存储,存储要求从高分到低分的顺序排列。 
map的使用场景:比如按ID号存储十万个用户,想要快速要通过ID查找对应的用户。二叉树的查找效率,这时就体现出来了。如果是vector容器,最坏的情况下可能要遍历完整个容器才能找到该用户。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一份讲解全面的标准模板STL学习资料 标准模板STL主要由6大组件组成: (1)容器(Containers)。包括各种基本数据结构的类模板STL容器部分主要由头文件<vector>、<list>、<deque>、<set>、< map>、<stack>和<queue>组成。 (2)算法(Algorithms)。包括各种基本算法,如比较、交换、查找、排序、遍历操作、复制、修改、移除、反转、合并等等。 STL算法部分主要由头文件<algorithm>和<numeric>组成。 (3)迭代器(Iterators)。迭代器是面向对象版本的指针,如同指针可以指向内存中的一个地址,迭代器可以指向容器中的一个位置。 STL的每一个容器类模板中,都定义了一组对应的迭代器类,用以存取容器中的元素。这样,在STL中迭代器就将算法和容器联系起来了,通过迭代器,算法函数可以访问容器中指定位置的元素,而无需关心元素的具体类型。 STL迭代器部分主要由头文件<utility>和<iterator>组成。 (4)函数对象(Function Objects)。一种行为类似于函数的class,实现技术上是一个改写了“call operator()”的class。 STL 提供 15 个预定义的 Function objects。头文件<functional>中定义了一些类模板,用以声明函数对象。 (5)适配器(Adaptors)。简单地说就是一种接口类,专门用来修改现有类的接口,提供一种新的接口;或调用现有的函数来实现所需要的功能。 主要包括3种适配器Container Adaptors、Iterator Adaptors与Function Adaptors。其中迭代器适配器的定义在头文件<iterator>中,函数适配器的定义在头文件<functional>中。 (6)内存配置器(Allocators)。为STL提供空间配置的系统。 头文件<memory>中的主要部分是模板类allocator,它负责产生所有容器中的默认分配器。容器使用allocator完成对内存的操作,allocator提供内存原语以对内存进行统一的存取。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值