STL简介:
STL(Standard Template Library),即标准模板库,是一个具有工业强度的,高效的C++程序库。它被容纳于C++标准程序库(C++ Standard Library)中,是ANSI/ISO C++标准中最新的也是极具革命性的一部分。该库包含了诸多在计算机科学领域里所常用的基本数据结构和基本算法。为广大C++程序员们提供了一个可扩展的应用框架,高度体现了软件的可复用性。
STL中六大组件:
1)容器(Container),是一种数据结构,如list,vector,和deques ,以模板类的方法提供。为了访问容器中的数据,可以使用由容器类输出的迭代器;
2)迭代器(Iterator),提供了访问容器中对象的方法。例如,可以使用一对迭代器指定list或vector中的一定范围的对象。迭代器就如同一个指针。事实上,C++的指针也是一种迭代器。但是,迭代器也可以是那些定义了operator*()以及其他类似于指针的操作符地方法的类对象;
3)算法(Algorithm),是用来操作容器中的数据的模板函数。例如,STL用sort()来对一个vector中的数据进行排序,用find()来搜索一个list中的对象,函数本身与他们操作的数据的结构和类型无关,因此他们可以在从简单数组到高度复杂容器的任何数据结构上使用;
4)仿函数(Function object)
5)迭代适配器(Adaptor)
6)空间配制器(allocator)
下面主要为大家讲解STL容器部分内容:
1、vector:
vector(向量):是一种顺序容器,事实上和数组差不多,但它比数组更优越。一般来说数组不能动态拓展,因此在程序运行的时候不是浪费内存,就是造成越界。而vector正好弥补了这个缺陷,它的特征是相当于可分配拓展的数组,它的随机访问快,在中间插入和删除慢,但在末端插入和删除快。
下面附上代码,部分接口以及相应的操作已经在注释中给出:
#include
#include
#include
#include
#include
2、list:
list容器实质是一个双向链表,list不提供随机访问功能,当删除其中一个元素,指向其他元素的迭代器依然有效。对于任何位置的元素插入或移除,list永远是常数时间,即list可以高效的进行插入删除元素。
下面附上代码,接口及应用在代码注释中给出:
//list的简单应用
//list是一个双向链表,可以高效的进行插入删除元素。
int main()
{
lista{ 1,2,3,4};
list::iterator it;
//a.begin() 返回指向第一个元素的迭代器
//a.end() 返回指向最后一个元素的迭代器
for (it = a.begin(); it != a.end(); it++) {
cout << *it << "\t";
}
system("pause");
return 0;
}
//c.rbegin() 返回逆向链表的第一个元素,即c链表的最后一个数据。
//c.rend() 返回逆向链表的最后一个元素的下一个位置, 即c链表的第一个数据再往前的位置。
int main()
{
lista{ 1,2,3,4,5 };
list::reverse_iterator it;
for (it = a.rbegin(); it != a.rend(); it++)
cout << *it << "\t";
system("pause");
return 0;
}
int main()
{
///operator= 重载赋值运算符
lista1{ 1,2,3,4,5 }, a2;
a2 = a1;
list::iterator it;
for (it = a2.begin(); it != a2.end(); it++)
{
cout << *it << endl;
}
//c.assign(n,num) 将n个num拷贝给链表c
//c.assign(beg,end) 将[beg,end]区间的元素拷贝赋值给链表c
int a[5] = { 1,2,3,4,5 };
list a1;
list::iterator it;
a1.assign(2, 10);
for (it = a1.begin(); it != a1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
a1.assign(a, a + 5);
for (it = a1.begin(); it != a1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
//c.front() 返回链表的第一个元素
//c.back() 返回链表的最后一个元素
list a1{ 1,2,3,4,5 };
if (!a1.empty())
{
cout << "first:" << a1.front() << endl;
cout << "last:" << a1.back() << endl;
}
//c.empty() 判断链表是否为空
//c.size() 返回链表中实际的个数
//c.clear() 清除链表c中的所有元素
//c.insert(pos,num) 在pos位置插入元素num
//c.insert(pos,n,num) 在pos位置插入n个元素num
//c.insert(pos,beg,end) 在pos位置插入区间为[beg,end]的元素
list a1{ 1,2,3,4,5 };
list::iterator it;
cout << "insert before:";
for (it = a1.begin(); it != a1.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
//
a1.insert(a1.begin(), 0);
cout << "insert after:";
for (it = a1.begin(); it != a1.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
//
a1.insert(a1.begin(), 3, 1);
cout << "insert after:";
for (it = a1.begin(); it != a1.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
//
int arr[] = { 88,66,99,55,77 };
a1.insert(a1.begin(), arr, arr + 3);
cout << "insert after:";
for (it = a1.begin(); it != a1.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
//c.erase(pos) 删除pos位置上的元素
list a1{ 1,2,3,4,5 };
list::iterator it;
cout << "erase before:";
for (it = a1.begin(); it != a1.end(); ++it)
{
cout << *it << " ";
}
a1.erase(a1.begin());
cout << "erase after:";
for (it = a1.begin(); it != a1.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
//c.push_back(num) 在末尾增加一个元素。
//c.pop_back() 删除末尾的元素。
//c.push_front(num) 在开始位置增加一个元素。
//c.pop_front() 删除第一个元素。
list a1{ 1,2,3,4,5 };
a1.push_back(10);
list::iterator it;
for (it = a1.begin(); it != a1.end(); ++it)
{
cout << *it << " ";
}
//resize(n) 从新定义链表的长度,超出原始长度部分用0代替,小于原始部分删除。
//resize(n, num) 从新定义链表的长度, 超出原始长度部分用num代替。
//c1.swap(c2); 将c1和c2交换。
//c1.merge(c2) 合并2个有序的链表并使之有序,从新放到c1里,释放c2。
//c1.merge(c2, comp)合并2个有序的链表并使之按照自定义规则排序之后从新放到c1中, 释放c2。
//
list a1{ 1,2,3 }, a2{ 4,5,6 };
a1.merge(a2);
list::iterator it;
cout << "a1.merge(a2):";
for (it = a1.begin(); it != a1.end(); it++) {
cout << *it << " ";
}
cout << endl;
//
a2.merge(a1, [](int n1, int n2) {return n1>n2; });
cout << "a2.merge(a1,comp):";
for (it = a2.begin(); it != a2.end(); it++) {
cout << *it << " ";
}
cout << endl;
//reverse() 反转链表
list a1{ 1,2,3,4,5 };
a1.reverse();
list::iterator it;
for (it = a1.begin(); it != a1.end(); ++it)
{
cout << *it << " ";
}
//unique() 删除相邻的元素
//c.sort() 将链表排序,默认升序
//c.sort(comp) 自定义回调函数实现自定义排序
//
system("pause");
return 0;
}
3、deque:
deque容器与vector类似,支持随机访问和快速插入删除,它还支持从开始端插入数据。Vector是单向开口的连续线性空间,deque则是一种双向开口的连续线性空间。deque对象在队列的两端放置元素和删除元素是高效的,而向量vector只是在插入序列的末尾时操作才是高效的。deque和vector的最大差异,一在于deque允许于常数时间内对头端进行元素的插入或移除操作,二在于deque没有所谓的capacity观念,因为它是动态地以分段连续空间组合而成,随时可以增加一段新的空间并链接起来。
下面附上代码:
//deque容器
//deque容器与vector类似,支持随机访问和快速插入删除,它还支持从开始端插入数据。
构造函数
deque c 创建一个空的deque
deque c1(c2) 复制一个deque
deque c(n) 创建一个deque,含有n个数据,数据均以缺省构造产出
deque c(n, elem) 创建一个含有n个elem拷贝的deque
deque c(beg, end) 创建一个以[beg; end)区间的deque
~deque() 销毁所有数据,释放内存
int main()
{
//举例说明:
//c.begin() 返回指向第一个元素的迭代器
//c.end() 返回指向最后一个元素下一个位置的迭代器
deque d{ 1,2,3,4,5 };
deque::iterator it;
for (it = d.begin(); it != d.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
//c.rbegin()返回指向反向队列的第一个元素的迭代器(即原队列的最后一个元素)
//c.rend()返回指向反向队列的最后一个元素的下一个位置(即原队列的第一个元素的前一个位置)
deque d{ 1,2,3,4,5 };
deque::reverse_iterator it;
for (it = d.rbegin(); it != d.rend(); ++it)
{
cout << *it << " ";
}
cout << endl;
//operator = 赋值运算符重载
deque d1{ 1,2,3,4,5 }, d2;
d2 = d1;
deque::iterator it;
for (it = d2.begin(); it != d2.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
cout << d2.size() << endl;
//c.assign(n,num)将n个num拷贝复制到容器c
//c.assign(beg, end)将[beg, end)区间的数据拷贝复制到容器c
//c.operator[]下标运算符重载
//c.empty()判断c容器是否为空
//c.front()返回c容器的第一个元素
//c.back()返回c容器的最后一个元素
//c.size()返回c容器中实际拥有的元素个数
//c.push_back(num)在末尾位置插入元素
//c.pop_back()删除末尾位置的元素
//c.push_front(num)在开头位置插入元素
//c.pop_front()删除开头位置的元素
deque d{ 1,2,3,4,5 };
d.push_back(20);
d.push_front(0);
deque::iterator it;
for (it = d.begin(); it != d.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
//c.resize(num)从新定义容器的大小
deque d{ 1,2,3,4,5 };
d.resize(d.size() + 5 );
deque::iterator it;
for (it = d.begin(); it != d.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
system("pause");
return 0;
}
4、stack:
stack是一种“先进后出”的数据结构,它只能在栈顶对数据进行操作,即只能在栈顶进行新增元素、移除元素、取得最顶端元素。不能进行遍历行为,所以不需要设计自己的迭代器。在SGI STL的源码
下面附上部分代码:
////stack堆栈容器
////默认使用的是双端队列结构, 是一个容器类的改编,为程序员提供了堆栈的全部功能,—
////也就是说实现了一个先进后出(FILO)的数据结构。
//1.empty() 堆栈为空则返回真
//2.pop() 移除栈顶元素
//3.push() 在栈顶增加元素
//4.size() 返回栈中元素数目
//5.top() 返回栈顶元素
class Student {
public:
int age;
char name[30];
};
void ProtectA() {
//跟vector不同,不需要初始化初始元素个数
stack ss1;
Student s1, s2, s3;
s1.age = 12;
strcpy(s1.name, "小米");
s2.age = 14;
strcpy(s2.name, "小红");
s3.age = 16;
strcpy(s3.name, "小刚");
//添加元素
ss1.push(&s1);
ss1.push(&s2);
ss1.push(&s3);
//弹出栈顶元素
while (!ss1.empty()) {
//获取栈顶元素
Student * temp = ss1.top();
cout << "学生姓名:" << temp->name << ";学生年龄是:" << temp->age << endl;
//弹出栈顶元素
ss1.pop();
}
}
其它相关:
4、vector和deque以及list的区别?
vector:连续存储结构,每个元素在内存中是连续的,支持高效的随机存储和在尾端插入/删除操作,但其他位置插入删除效率低下;deque:连续存储结构,类似于vector,不同的是 deque提供了两级数组结构,还支持收尾端高效的插入删除操作。deque优点:随机访问方便,即支持[]操作符和list;在内部方便的进行插入和删除操作;可以两端进行push pop。缺点:占用内存多。
使用区别:
List: 非连续存储结构,具有双链表结构,每个元素维护一对前向和后向指针,因此支持前向/后向遍历。支持高效的随机插入/删除操作,但随机访问效率低下,且由于需要额外维护指针,开销也比较大。
优点: (1) 不使用连续内存完成动态操作。(2) 在内部方便的进行插入和删除操作 (3) 可在两端进行push、pop
缺点:(1) 不能进行内部的随机访问,即不支持[ ]操作符和vector.at (2) 相对于verctor占用内存多。