STL — это мощный набор обобщённых контейнеров, алгоритмов и итераторов, встроенный в стандарт C++. Она делает разработку более эффективной и безопасной.
Основные компоненты STL:
- Контейнеры:
vector
,list
,deque
,map
,set
,unordered_map
, и др. - Алгоритмы:
sort
,find
,for_each
,accumulate
и др. - Итераторы: позволяют универсальный доступ к элементам контейнеров.
- Функциональные объекты (функторы) и лямбда-функции.
- Адаптеры контейнеров и функций:
stack
,queue
,priority_queue
.
Пример: простой vector
#include <iostream> #include <vector> int main() { std::vector<int> numbers = {1, 2, 3, 4}; numbers.push_back(5); for (int n : numbers) { std::cout << n << " "; } return 0; }
std::vector — универсальный динамический массив
std::vector
— это самый популярный контейнер STL. Он работает как массив с возможностью динамического изменения размера.
Примеры использования:
std::vector<std::string> names = {"Alice", "Bob", "Charlie"}; names.emplace_back("David"); // эффективнее, чем push_back names.erase(names.begin() + 1); // удаление второго элемента
reserve(n)
— резервирует память, снижает количество реаллокаций.
shrink_to_fit()
— уменьшает выделенную память до фактического размера.
std::map и std::unordered_map — ассоциативные контейнеры
std::map<std::string, int> ages; ages["Alice"] = 30; ages["Bob"] = 25; for (auto& [name, age] : ages) { std::cout << name << ": " << age << "\n"; }
std::unordered_map (на основе хеш-таблицы):
std::unordered_map<int, std::string> lookup = {{1, "один"}, {2, "два"}}; if (lookup.contains(2)) { std::cout << "есть ключ 2"; }
Когда использовать:
map
— если важен порядок ключей.unordered_map
— если важна скорость доступа.
STL алгоритмы — мощные инструменты над контейнерами
#include <algorithm> #include <numeric> // accumulate std::vector<int> v = {1, 2, 3, 4, 5}; std::sort(v.begin(), v.end(), std::greater<int>()); int sum = std::accumulate(v.begin(), v.end(), 0); bool allPositive = std::all_of(v.begin(), v.end(), [](int n){ return n > 0; });
Полезные алгоритмы:
std::find
,std::count
,std::any_of
,std::none_of
std::reverse
,std::rotate
,std::partition
Итераторы в STL и как они работают
Типы итераторов:
InputIterator
OutputIterator
ForwardIterator
BidirectionalIterator
RandomAccessIterator
Пример итерации:
std::vector<int> v = {10, 20, 30}; for (auto it = v.begin(); it != v.end(); ++it) { std::cout << *it << " "; }
Специальные итераторы:
std::back_inserter()
,std::istream_iterator
,std::ostream_iterator
Адаптеры STL — stack, queue, priority_queue
#include <stack> #include <queue> #include <iostream> std::stack<int> st; st.push(10); st.push(20); st.pop(); // LIFO std::queue<int> q; q.push(1); q.push(2); q.pop(); // FIFO std::priority_queue<int> pq; pq.push(100); pq.push(50); pq.push(200); // max-heap
std::set и std::multiset
std::set — это контейнер, хранящий уникальные элементы в отсортированном порядке.
std::multiset — позволяет хранить повторяющиеся элементы.
#include <set> std::set<int> numbers = {3, 1, 4, 1, 5, 9}; numbers.insert(2); // только уникальные значения for (int n : numbers) { std::cout << n << " "; } // Вывод: 1 2 3 4 5 9
std::multiset<int> values = {10, 10, 20, 20, 30}; values.insert(10); // можно вставить ещё одно 10 std::cout << values.count(10); // 3
std::deque — двусторонняя очередь
std::deque (double-ended queue) позволяет быстро вставлять и удалять элементы как в начале, так и в конце.
#include <deque> std::deque<int> d = {1, 2, 3}; d.push_front(0); // [0, 1, 2, 3] d.push_back(4); // [0, 1, 2, 3, 4] d.pop_back(); // [0, 1, 2, 3]
Преимущество над vector
— эффективная вставка в начало.
Как писать собственные алгоритмы STL-стиля
template<typename Iterator> void printRange(Iterator begin, Iterator end) { for (; begin != end; ++begin) { std::cout << *begin << " "; } } std::vector<int> v = {10, 20, 30}; printRange(v.begin(), v.end());
STL и многопоточность — что важно знать
STL-контейнеры не потокобезопасны по умолчанию, но их можно использовать при правильной синхронизации:
// Потокобезопасный доступ: #include <mutex> std::mutex mtx; std::vector<int> data; void addValue(int value) { std::lock_guard<std::mutex> lock(mtx); data.push_back(value); }
// Очередь между потоками: std::queue<int> q; std::mutex m; std::condition_variable cv; void producer() { std::unique_lock<std::mutex> lock(m); q.push(42); cv.notify_one(); } void consumer() { std::unique_lock<std::mutex> lock(m); cv.wait(lock, []{ return !q.empty(); }); int val = q.front(); q.pop(); }