序列式容器的数据结构是线性结构
中文名 | 英文名 |
---|---|
向量 | vector |
双向链表 | list |
双端队列 | deque |
关联式容器的数据结构是非线性结构
中文名 | 英文名 |
---|---|
集合 | set |
多重集合 | multiset |
映射 | map |
多重映射 | multimap |
适配器用于适配容器,作为特殊用途的容器,允许自由指定接口匹配的容器。
中文名 | 英文名 | 默认容器 |
---|---|---|
栈 | stack | deque |
队列 | queue | deque |
优先队列 | priority_queue | vector |
下述所有示例以下标例子为模板,其改动只有函数print和头文件。
下标适用于遍历连续性的线性结构容器,对于非连续性的线性结构和非线性结构容器,可以重载operator []或者增加接口函数,并且构建索引表访问对应元素。
以vector和map为例:
#include
#include
using DynamicArray = std::vector<long>;
void print(const DynamicArray &dynamicArray)
{
using std::cout;
for (DynamicArray::size_type index = 0; index < dynamicArray.size(); ++index)
cout << dynamicArray[index] << ' ';
cout << dynamicArray.size();
}
int main()
{
DynamicArray dynamicArray = { 0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L };
print(dynamicArray);
return 0;
}
#include
#include
#include
#include
int main()
{
using std::string;
using StringArray = std::vector<string>;
StringArray keys = { "Eternal", "ZGMF-X09A", "ZGMF-X10A" };
std::map<string, string> map = { {keys[0], "Meteor"}, {keys[1], "Justice"}, {keys[2], "Freedom"} };
for (StringArray::size_type index = 0; index < keys.size(); ++index)
{
const auto &key = keys[index];
std::cout << key << ' ' << map[key] << std::endl;
}
return 0;
}
STL由容器、迭代器、算法三层结构组成,为不同的容器定制接口相同的迭代器,线性结构与非线性结构的容器都能够通过迭代器进行遍历。
#include
#include
using DynamicArray = std::vector<long>;
void print(const DynamicArray &dynamicArray)
{
using std::cout;
for (auto iterator = dynamicArray.cbegin(); iterator != dynamicArray.cend(); ++iterator)
cout << *iterator << ' ';
cout << dynamicArray.size();
}
int main()
{
DynamicArray dynamicArray = { 0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L };
print(dynamicArray);
return 0;
}
对于一些容器,如果使用迭代器遍历,向容器添加或删除元素,将导致迭代器失效(不同容器的迭代器失效的时机不同),后续遍历操作是未定义行为,因此每次在插入或删除元素之后都应该重新定位迭代器。
算法由于以迭代器为基础,因此对线性结构和非线性结构的容器都适用,以for_each和copy为例:
引用头文件algorithm
#include
#include
#include
using DynamicArray = std::vector<long>;
void print(const DynamicArray &dynamicArray)
{
using std::cout;
std::for_each(dynamicArray.cbegin(), dynamicArray.cend(),
[](const DynamicArray::value_type &element)
{
cout << element << ' ';
});
cout << dynamicArray.size();
}
int main()
{
DynamicArray dynamicArray = { 0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L };
print(dynamicArray);
return 0;
}
引用头文件algorithm和iterator
#include
#include
#include
#include
using DynamicArray = std::vector<long>;
void print(const DynamicArray &dynamicArray)
{
using std::cout;
std::copy(dynamicArray.cbegin(), dynamicArray.cend(),
std::ostream_iterator<DynamicArray::value_type>(cout, " "));
cout << dynamicArray.size();
}
int main()
{
DynamicArray dynamicArray = { 0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L };
print(dynamicArray);
return 0;
}
C++11标准新增range-for语法,其底层实现依赖于迭代器,对于线性结构和非线性结构的容器通用。
#include
#include
using DynamicArray = std::vector<long>;
void print(const DynamicArray &dynamicArray)
{
using std::cout;
for (const auto &element : dynamicArray)
cout << element << ' ';
cout << dynamicArray.size();
}
int main()
{
DynamicArray dynamicArray = { 0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L };
print(dynamicArray);
return 0;
}
当使用range-for遍历容器时,不可以减少容器容量,也不可以任意添加或者删除元素,只有在迭代器不失效的情况下可以进行这些操作。
C++标准并不支持以下语法:
for each (auto var in collect) { visit(var); }
Visual Studio中使用C++17标准,编译此语法代码,提示下述错误信息:
error C4496: 使用了非标准扩展“for each”: 替换为 ranged-for 语句
Qt运用宏模仿此语法:
QStringList words = {"freedom", "justice"};
foreach (QString word, words) { qDebug() << word; }
C#也有类似用法,其语法格式如下:
foreach (type object in collection/array) {
visit(object);
}
调用算法相比于手写循环,具有以下优势:
STL算法使用复杂的计算机科学算法,有些算法非常复杂,并非一般程序员所能够达到。
类库实现者比使用者更清楚内部实现细节,能够根据对于容器实现的了解程度优化遍历过程,这是库的使用者难以做到的事情。
如果代码作用与算法功能相近,调用算法有利于提高代码清晰度。但是,若循环代码简单清晰,用以显示实现细节,而调用算法可能需要混合绑定器和配接器,或者封装单独的函数子类,则手写循环更合适。
[1] Effective STL 中文版:50条有效使用STL的经验 / (美) 梅耶 (Meyers,S.) 著;潘爱民,陈铭,邹开红译. --北京:电子工业出版社,2013.5