C++ 基础(2):STL Containers
本文首先介绍了 C++ STL 常用的容器,分别介绍了对应 Python 中的 list(), dict() 的容器,并且对于各容器的优缺点进行了总结;然后介绍了遍历容器的迭代器 Iterator,最后略微提及了可以指向所有类型的指针。
- 顺序容器:
std::vector,std::deque,std::array - 关联容器(dict):
std::map/std::set,std::unordered_map/std::unordered_set - Iterator
本文仅是对于相关容器的介绍,具体一图流 API 整理在 C++ 常用 API 一图流.
Containers
Sequence containers
std::vector
std::vector<T>.
- Contiguous memory
- random memory access
- Insertion/removal is efficient at the end of the container

std::deque
std::deque<T>: Double-ended queue. Allows efficient insertion/removal at either end
Note. The memory is no longer contiguous. 
APIs
Access: [i], .at(i), .front(), .back()
Modify (Insert/Delete):
.push_back(x),.pop_back(),.emplace_back(x)- Only deque:
.push_front(x),.pop_front(),.emplace_front(x)Insert (Slow): v.insert(v.begin() + 2, x)v.erase(v.begin() + 2, v.begin() + 5)-> Iterator
Others
Others: std::array<T, N> (Example: std::array<std::string, 5>) More efficienct than std::vector since it’s memory size is known at compile time thus can be stack allocated.
Associative containers
std::map
std::map illustration
std::map<K, V> stores a collection of std::pair<const K, V>. K should be const.
std::map<std::string, int> map {{"Alice", 1}, {"Bob", 2}};
for (auto kv:map) {
std::string key = kv.first;
int value = kv.second;
}
for (const auto &[key, value] : map) {
// ...
}std::map<K, V> is implemented as a BST (technically a red-black tree), therefore requires K to have an operator< (K, not V!).
APIs:
- Insert:
m[k]=v,m.insert({k, v}),m.emplace(k, v) - Search:
if m.contains(k) - Check empty:
if (m.empty())
std::set
std::set<K>
- is conceptually similar to
std::map, which is also a BST - is an
std::mapwithout values - also requires
Kto have an operator<
Unordered map and Unordered set
Unordered map and unordered set is implemented as a Hashmap.
std:unordered_map illustration
std::unordered_map<K, V> and std::unordered_set<K>
- do not require
Kto haveoperator< - require
Kto be hashable
APIs
Access: m[k]
Modify (Append/Delete):
m.insert({k, v}),m[k] = vs.insert(k)m.erase(k)ors.erase(k)
Check if k in the map/set:
if (m.contains(k))orif (s.contains(k))
Check if m or s is empty:
if (m.empty())orif (s.empty())
Summary of data structures

Iterators
To iterate elements in a certain container, e.g. std::deque, std::map, std::set, the standard usage is:
for (const auto& elem : container) { ... }C++ iterators are like a “claw” in a claw machine.
The claw can:
- Grab a toy
- Move forward
- Check if we’re done
To move the claw, we need to know:
- Where to start
- Where to stop
C++ iterator example
Basic Interfaces
Container interfaces:
c.begin(): Iterator to the first elementc.end(): Iterator to one element after the end of the container
Iterator interfaces:
auto it = c.begin()it++- Dereference:
auto &elem = *it, and moreoverauto &x = it->member - Forward pass
++it(Better thanit++)
Note (Difference between ++it and it++).
- Prefix
++it: Increments the iterator and returns a reference to the same object - Postfix
it++: Increments it and returns a copy of the old value - Better to use prefix than postfix to avoid unnecessary value copy.
Iterator Types
All iterators support the basic operations.
However, some provide even more:
- Move backwards:
--it; - Modify:
*it = elem; - Randn. access:
it += n; - Compare positions:
it1 < it2
Types of iterators:
- Input iterators. Allow us to read elements:
auto elem = *it; - Output iterators. Allow us to write elements:
*it = elem; - Forward iterators: Allows us to make multiple passes. If
it1 == it2, then++it1 == ++it2(Counterexample: streams, when forwarding, consumes the elments in the container) - Bidirectional iterations: Allow us to move both forward and backward.
auto& elem = *(--it); - Random access iterators: Allow us to quickly skip forward and backward.
auto & second = *(it + 2);
Summary of STL iterator types

Pointers
An iterator points to a container element, a pointer points to any object
Example.
int*meanspxis a pointer to anint&is the address of the operator (in this case,x)
int x = 106;
int* px = &x;