C/C++ Ready | Программирование
15K subscribers
1.42K photos
92 videos
701 links
Авторский канал по разработке на C и C++.
Ресурсы, гайды, задачи, шпаргалки.
Информация ежедневно пополняется!

Автор: @energy_c

РКН: https://clck.ru/3QREHc

Реклама на бирже: https://telega.in/c/cpp_ready
Download Telegram
Почему из map нельзя удалять элементы как попало во время обхода?

Иногда нужно пройтись по контейнеру и удалить часть элементов. Например, убрать просроченные сессии, пустые записи, старые токены или временные ключи после фоновой очистки.

На первый взгляд хочется написать обычный цикл.
for (auto it = cache.begin(); it != cache.end(); ++it) {
if (it->second.expired()) cache.erase(it);
}


Выглядит логично. Мы стоим на элементе, проверяем его и удаляем, если он больше не нужен.

Но после erase(it) итератор it становится невалидным. А в конце итерации цикл всё равно попробует выполнить ++it.

То есть программа двигает уже сломанный итератор. Такое поведение может проявиться не сразу, а только на некоторых данных или сборках.

Правильный паттерн такой.
for (auto it = cache.begin(); it != cache.end(); ) {
if (it->second.expired()) {
it = cache.erase(it);
} else {
++it;
}
}


erase возвращает итератор на следующий элемент. Поэтому после удаления мы не делаем ++it вручную, а сразу продолжаем обход с корректной позиции.

Если элемент не удалили, тогда обычный ++it нужен. Именно поэтому инкремент убирают из заголовка for.

Для std::map и std::unordered_map это особенно важно, когда очистка идёт по условию внутри цикла.

Если нужно просто удалить всё по предикату и у вас C++20, можно использовать std::erase_if.
std::erase_if(cache, [](const auto& item) {
return item.second.expired();
});


Но явный цикл всё равно полезно знать. Он нужен, когда вместе с удалением нужно логировать, считать статистику или выполнять дополнительное действие.

Главная мысль простая. Если удаляешь элемент через итератор во время обхода, следующий итератор должен прийти из erase, а не из ++ после удаления.

Есть ещё одна частая ошибка. Иногда удаление пытаются спрятать внутрь range-for.

Так делать не стоит.
for (auto& [key, value] : cache) {
if (value.expired()) cache.erase(key);
}


Range-for сам управляет итератором внутри цикла. Если контейнер меняется во время обхода, этот внутренний итератор тоже может стать невалидным.

Поэтому для удаления по условию лучше сразу писать явный итераторный цикл. Он чуть длиннее, зато в нём видно, где именно происходит переход к следующему элементу.

Если внутри удаления есть дополнительные действия, их тоже удобно держать рядом.
if (it->second.expired()) {
log_removed(it->first);
it = cache.erase(it);
}


Такой код проще читать при ревью. Видно и условие удаления, и побочный эффект, и безопасное обновление итератора.

📣 C++ Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍94🔥3
Интересный разбор архитектуры сцен в графических движках!

В статье автор разбирает, почему классический Scene Graph не всегда хорошо ложится на современные render pipeline, explicit API и задачи с большим количеством объектов.

В статье автор показывает:
• почему древовидная сцена может мешать производительности
• как думать о данных, зависимостях и проходах рендера
• почему современному движку часто нужен более гибкий подход

Продолжай читать на Habr!


📣 C++ Ready | #статья
Please open Telegram to view this post
VIEW IN TELEGRAM
7👍5🔥4
Полезная статья про автоматическую проверку архитектуры C++-проекта!

Автор разбирает инструмент archcheck, который помогает описать правила зависимостей между модулями и проверять, что кодовая база не превращается в хаотичный клубок include-связей.

В статье автор показывает:
• как описывать допустимые зависимости между частями проекта
• как находить нарушения архитектурных границ
• почему такие проверки полезно запускать в CI

Продолжай читать на Habr


📣 C++ Ready | #статья
Please open Telegram to view this post
VIEW IN TELEGRAM
👍65🔥4😁1
Считаем размер папки через std::filesystem!

Иногда нужно быстро понять, сколько места занимает директория. Например, перед очисткой, анализом или проверкой файлов.

В C++17 для обхода файловой системы есть std::filesystem.

Подключим нужные заголовки:
#include <filesystem>
#include <iostream>


Для удобства заведём namespace:

namespace fs = std::filesystem;


Функция будет принимать путь к папке:

std::uintmax_t dir_size(const fs::path& root) {


Сначала заведём счётчик байт:

std::uintmax_t total = 0;


Теперь рекурсивно обойдём директорию:

for (const auto& entry : fs::recursive_directory_iterator(root)) {


Нас интересуют только обычные файлы. Папки и ссылки можно пропустить:

if (!entry.is_regular_file()) continue;


Размер файла добавляем в общий счётчик:

total += entry.file_size();


После обхода возвращаем результат:

return total;
}


Вызов может выглядеть так:

auto bytes = dir_size("logs");
std::cout << bytes << "\n";


В реальном коде стоит помнить про права доступа. Если часть файлов нельзя прочитать, можно использовать перегрузки с std::error_code, чтобы программа не падала на первом недоступном каталоге.

📣C++ Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
8👍5🔥3
Разбираем std::format и форматирование строк!

std::format помогает собирать строки без цепочек operator<< и без ручной склейки. Форматирование становится ближе к шаблону, где видно итоговую форму текста.

В этой шпоре собраны format, format_to, formatted_size, format_to_n, std::print, chrono formatting и custom formatter.

📣 C++ Ready | #шпора
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍43🤝1
Шпаргалка по range views в C++20 и C++23!

Например, views::filter оставляет только подходящие элементы, views::transform применяет функцию к каждому значению, а views::zip и views::chunk помогают собирать более сложные ленивые цепочки обработки.

Сохрани, чтобы не потерять!

📣 C++ Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
👍75🔥4
Собираем потокобезопасную очередь на C++!

Нужно передавать задачи между потоками так, чтобы один поток мог добавлять элементы, а другой спокойно ждать новые данные без активного цикла и лишней нагрузки на CPU.

В этой задаче:
• храним queue под mutex
• будим ожидающий поток через condition_variable
• достаём элементы через wait_and_pop


Такую основу можно использовать для worker-пула, фоновой обработки логов, очереди событий или простого producer-consumer сценария.

📣 C++ Ready | #задачу
Please open Telegram to view this post
VIEW IN TELEGRAM
10👍8🔥4
Напоминалка по lambda expressions в C++!

Например, capture by reference позволяет лямбде работать с внешней переменной напрямую, capture by value сохраняет копию значения, а mutable даёт менять захваченную копию внутри лямбды.

На картинке собраны основные приёмы работы с lambda expressions. Есть захваты по ссылке и по значению, init capture, immediately invoked lambdas, mutable lambdas, generic lambdas, perfect forwarding и возможности C++20.

Сохрани, чтобы не потерять!

📣 C++ Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
7👍6🔥3
Разбираем std::variant: как хранить разные типы без unsafe union!

std::variant помогает описывать значения, которые могут быть одним из нескольких заранее известных типов. Это удобно для парсеров, состояний, команд, ошибок и ответов API: данные остаются типобезопасными, а компилятор помогает не забыть обработать нужные варианты.

📣 C++ Ready | #шпора
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥115👍3