完成 2.11 容器和模板
Signed-off-by: lion187 <cy187lion@sina.com>
This commit is contained in:
parent
e085eba5fe
commit
cfbf478914
@ -5,7 +5,7 @@ C++ 提供了一些用于泛型编程的工具,包括各种标准容器、迭
|
||||
|
||||
## 2.11.1 C++ 标准模板库 STL
|
||||
|
||||
C++ 标准模板库,提供了很多常用的容器和算法。以往在 C 语言中需要自己实现的排序算法,在 C++ 中只要使用 Vector 容器和 sort() 方法即可轻松实现。
|
||||
C++ 标准模板库,提供了很多常用的容器和算法。以往在 C 语言中需要自己实现的排序算法,在 C++ 中只要使用 vector 容器和 sort() 方法即可轻松实现。
|
||||
|
||||
就像算法常常以数据结构为基础一样,STL 中的算法,主要围绕容器来实现。而容器则是以模板为基础实现的。最常用的有 10 大标准容器,分为 3 大类:
|
||||
|
||||
@ -32,15 +32,194 @@ C++ 标准模板库,提供了很多常用的容器和算法。以往在 C 语
|
||||
|
||||
### 2.11.1.1 Vector
|
||||
|
||||
是一个线性顺序结构。相当于数组,但其大小可以不预先指定,并且自动扩展。它可以像数组一样被操作,由于它的特性我们完全可以将 vector 看作动态数组。
|
||||
|
||||
在创建一个 vector 后,它会自动在内存中分配一块连续的内存空间进行数据存储,初始的空间大小可以预先指定也可以由 vector 默认指定,这个大小即 capacity() 函数的返回值。当存储的数据超过分配的空间时 vector 会重新分配一块内存块,但这样的分配是很耗时的,在重新分配空间时它会做这样的动作:
|
||||
|
||||
1. 首先,vector 会申请一块更大的内存块;
|
||||
2. 然后,将原来的数据拷贝到新的内存块中;
|
||||
3. 其次,销毁掉原内存块中的对象(调用对象的析构函数);
|
||||
4. 最后,将原来的内存空间释放掉。
|
||||
|
||||
如果 vector 保存的数据量很大时,这样的操作一定会导致糟糕的性能(这也是 vector 被设计成比较容易拷贝的值类型的原因)。所以说 vector 不是在什么情况下性能都好,只有在预先知道它大小的情况下 vector 的性能才是最优的。
|
||||
|
||||
vector 的特点:
|
||||
|
||||
1. 指定一块如同数组一样的连续存储,但空间可以动态扩展。即它可以像数组一样操作,并且可以进行动态操作。通常体现在 push_back() pop_back();
|
||||
2. 随机访问方便,它像数组一样被访问,即支持[ ] 操作符和 vector.at();
|
||||
3. 节省空间,因为它是连续存储,在存储数据的区域都是没有被浪费的,但是要明确一点 vector 大多情况下并不是满存的,在未存储的区域实际是浪费的;
|
||||
4. 在内部进行插入、删除操作效率非常低,这样的操作基本上是被禁止的。Vector 被设计成只能在后端进行追加和删除操作,其原因是 vector 内部的实现是按照顺序表的原理;
|
||||
5. 只能在 vector 的最后进行 push 和 pop ,不能在 vector 的头进行 push 和pop ;
|
||||
6. 当动态添加的数据超过 vector 默认分配的大小时要进行内存的重新分配、拷贝与释放,这个操作非常消耗性能。 所以要 vector 达到最优的性能,最好在创建 vector 时就指定其空间大小。
|
||||
|
||||
### 2.11.1.2 List
|
||||
|
||||
是一个线性链表结构,它的数据由若干个节点构成,每一个节点都包括一个信息块(即实际存储的数据)、一个前驱指针和一个后驱指针。它无需分配指定的内存大小且可以任意伸缩,这是因为它存储在非连续的内存空间中,并且由指针将有序的元素链接起来。
|
||||
|
||||
由于其结构的原因,list 随机检索的性能非常的不好,因为它不像 vector 那样直接找到元素的地址,而是要从头一个一个的顺序查找,这样目标元素越靠后,它的检索时间就越长。检索时间与目标元素的位置成正比。
|
||||
|
||||
虽然随机检索的速度不够快,但是它可以迅速地在任何节点进行插入和删除操作。因为 list 的每个节点保存着它在链表中的位置,插入或删除一个元素仅对最多三个元素有所影响,不像 vector 会对操作点之后的所有元素的存储地址都有所影响,这一点是 vector 不可比拟的。
|
||||
|
||||
list 的特点:
|
||||
|
||||
1. 不使用连续的内存空间这样可以随意地进行动态操作;
|
||||
2. 可以在内部任何位置快速地插入或删除,当然也可以在两端进行 push 和pop;
|
||||
3. 不能进行内部的随机访问,即不支持[ ] 操作符和 list.at();
|
||||
4. 相对于 verctor 占用更多的内存。
|
||||
|
||||
### 2.11.1.3 Map
|
||||
|
||||
### 2.11.1.4 Set
|
||||
Map 属于关联容器,是一种非线性的树结构,具体的说采用的是一种比较高效的特殊的平衡检索二叉树。
|
||||
|
||||
Map 提供一种“键- 值”关系的一对一的数据存储能力。其“键”在容器中不可重复,且按一定顺序排列。
|
||||
|
||||
关联容器的特点是明显的,相对于顺序容器,有以下几个主要特点:
|
||||
|
||||
1. 其内部实现是采用非线性的二叉树结构,具体的说是红黑树的结构原理实现的;
|
||||
2. set 和 map 保证了元素的唯一性,mulset 和 mulmap 扩展了这一属性,可以允许元素不唯一;
|
||||
3. 元素是有序的集合,默认在插入的时候按升序排列。
|
||||
|
||||
## 2.11.2 迭代器
|
||||
|
||||
## 2.11.3 模板类
|
||||
迭代器是一个对象,可以循环访问 STL 容器中的元素,并提供对各个元素的访问。 STL 容器全都提供迭代器,以便算法可以采用标准方式访问其元素,而不必考虑用于存储元素的容器类型。
|
||||
|
||||
可以通过使用成员和全局函数(如 begin() 和 end())以及运算符(如 ++ 和 -- )向前或向后移动,来显式使用迭代器。 还可以通过范围 for 循环或(对于某些迭代器类型)下标运算符 [],来隐式使用迭代器。
|
||||
|
||||
在 STL 中,序列或范围的开头是第一个元素。 序列或范围的末尾始终定义为最后一个元素的下一个位置。
|
||||
|
||||
## 2.11.3 STL 容器示例
|
||||
|
||||
vector、list、map 以及迭代器的示例如下:
|
||||
|
||||
```cpp
|
||||
/**
|
||||
* @file main.cpp
|
||||
*/
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
void ExpOfVector(void)
|
||||
{
|
||||
vector<double> MyVec;
|
||||
|
||||
MyVec.push_back(3.14*2);
|
||||
MyVec.resize(0);
|
||||
MyVec.push_back(3.14);
|
||||
MyVec.push_back(3.14/2);
|
||||
MyVec.push_back(3.14/4);
|
||||
MyVec.push_back(3.14/8);
|
||||
MyVec.insert(MyVec.begin()+1, 5.0); // 在 MyVec 的第1个元素(从第0个算起)的位置插入数值为 5.0 的元素.
|
||||
|
||||
MyVec.pop_back();
|
||||
cout<<"Vector Size="<<MyVec.size()<<endl; // 输出 4.
|
||||
cout<<"Vector First="<<MyVec.front()<<endl; // 返回 MyVec 的第一个元素 , 即 3.14.
|
||||
cout<<"Vector Last="<<MyVec.back()<<endl; // 返回 MyVec 的最后一个元素, 即 3.14/4.
|
||||
cout<<"Vector At 1="<<MyVec.at(1)<<endl; // 返回 MyVec 的第二个元素, 即 5.0.
|
||||
|
||||
// 依次输出 MyVec 中的每个元素值.
|
||||
for(vector<double>::iterator itr=MyVec.begin();
|
||||
itr!=MyVec.end();
|
||||
itr++)
|
||||
{
|
||||
cout<<*itr<<endl;
|
||||
}
|
||||
|
||||
MyVec.clear();
|
||||
cout<<"Vector Size="<<MyVec.size()<<endl;
|
||||
cout<<"---------------------------------------"<<endl;
|
||||
}
|
||||
|
||||
void ExpOfList(void)
|
||||
{
|
||||
list<double> MyList(5);
|
||||
|
||||
MyList.clear();
|
||||
|
||||
MyList.push_front(3.14*2);
|
||||
MyList.push_front(3.14*3);
|
||||
MyList.push_front(3.14*4);
|
||||
MyList.push_back(3.14*1);
|
||||
|
||||
// 依次输出 MyList 中的每个元素值: 3.14*4、3.14*3、3.14*2、3.14*1.
|
||||
for(list<double>::iterator itr=MyList.begin();
|
||||
itr!=MyList.end();
|
||||
itr++)
|
||||
{
|
||||
cout<<*itr<<endl;
|
||||
}
|
||||
|
||||
MyList.pop_back();
|
||||
MyList.pop_front();
|
||||
|
||||
// 依次输出 MyList 中的每个元素值: 3.14*3、3.14*2.
|
||||
for(list<double>::iterator itr=MyList.begin();
|
||||
itr!=MyList.end();
|
||||
itr++)
|
||||
{
|
||||
cout<<*itr<<endl;
|
||||
}
|
||||
|
||||
MyList.insert(MyList.begin()++, 5.0);
|
||||
|
||||
// 依次输出 MyList 中的每个元素值: 5.0、3.14*3、3.14*2.
|
||||
for(list<double>::iterator itr=MyList.begin();
|
||||
itr!=MyList.end();
|
||||
itr++)
|
||||
{
|
||||
cout<<*itr<<endl;
|
||||
}
|
||||
|
||||
cout<<"Vector Size="<<MyList.size()<<endl;
|
||||
|
||||
cout<<"---------------------------------------"<<endl;
|
||||
}
|
||||
|
||||
void ExpOfMap(void)
|
||||
{
|
||||
map<int, string> MyMap;
|
||||
|
||||
MyMap.insert(map<int, string>::value_type(1, "Student1"));
|
||||
MyMap.insert(map<int, string>::value_type(2, "Student2"));
|
||||
MyMap[3] = "Student3";
|
||||
|
||||
MyMap.erase(2); // 删除 Key 值为 2 的元素.
|
||||
|
||||
map<int, string>::iterator itr;
|
||||
|
||||
itr = MyMap.find(3);
|
||||
if(itr != MyMap.end())
|
||||
{
|
||||
cout<<"The Key="<<itr->first<<endl;
|
||||
cout<<"The Value="<<itr->second<<endl;
|
||||
}
|
||||
else
|
||||
cout<<"Do not Find"<<endl;
|
||||
|
||||
itr = MyMap.find(2);
|
||||
if(itr != MyMap.end())
|
||||
{
|
||||
cout<<"The Key="<<itr->first<<endl;
|
||||
cout<<"The Value="<<itr->second<<endl;
|
||||
}
|
||||
else
|
||||
cout<<"Do not Find"<<endl;
|
||||
cout<<"---------------------------------------"<<endl;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
ExpOfVector();
|
||||
ExpOfList();
|
||||
ExpOfMap();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## 2.11.4 模板类
|
||||
|
||||
使用模板的目的就是能够让程序员编写与类型无关的代码。比如编写了一个交换两个整型 int 类型的 swap 函数,这个函数就只能实现 int 型,对 double,字符这些类型无法实现,要实现这些类型的交换就要重新编写另一个 swap 函数。使用模板的目的就是要让这程序的实现与类型无关,比如一个 swap 模板函数,即可以实现 int 型,又可以实现 double 型的交换。
|
||||
|
||||
@ -112,4 +291,6 @@ void main(){
|
||||
|
||||
## 练习
|
||||
|
||||
基于模板实现堆栈操作。
|
||||
1、 基于模板实现堆栈操作。
|
||||
|
||||
2、 使用 Vector 进行从小到大排序。
|
||||
|
Loading…
x
Reference in New Issue
Block a user