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_ofstd::reverse,std::rotate,std::partition
Итераторы в STL и как они работают
Типы итераторов:
InputIteratorOutputIteratorForwardIteratorBidirectionalIteratorRandomAccessIterator
Пример итерации:
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();
}
