RAII против goto cleanup: убираем утечки и ручную зачистку!
В “почти C”-коде на C++ часто открываешь ресурс, потом ещё один, а при ошибке нужно не забыть всё закрыть. Обычно это превращается в
Вот как это выглядит в начале: уже приходится помнить, что закрывать при неудаче:
Дальше появляется “ошибка в середине”, и чтобы не дублировать очистку, добавляют
А в конце — ручной порядок освобождения, который легко сломать правками:
В C++ это решает RAII: ресурс заворачиваем в объект, и он освобождается автоматически при выходе из области видимости.
Файл можно обернуть в
Буфер тоже делаем RAII-шным — и теперь любой ранний
🔥
📣 C++ Ready | #практика
В “почти C”-коде на C++ часто открываешь ресурс, потом ещё один, а при ошибке нужно не забыть всё закрыть. Обычно это превращается в
goto cleanup или дублирование free/fclose перед каждым return.Вот как это выглядит в начале: уже приходится помнить, что закрывать при неудаче:
FILE* f = std::fopen("data.txt", "r");
if (!f) return -1;
char* buf = (char*)std::malloc(1024);
if (!buf) { std::fclose(f); return -1; }Дальше появляется “ошибка в середине”, и чтобы не дублировать очистку, добавляют
cleanup:int rc = 0;
if (something_bad()) {
rc = -1;
goto cleanup;
}
А в конце — ручной порядок освобождения, который легко сломать правками:
cleanup:
std::free(buf);
std::fclose(f);
return rc;
В C++ это решает RAII: ресурс заворачиваем в объект, и он освобождается автоматически при выходе из области видимости.
Файл можно обернуть в
std::unique_ptr с deleter’ом:using FilePtr = std::unique_ptr<FILE, decltype(&std::fclose)>;
FilePtr f(std::fopen("data.txt", "r"), &std::fclose);
if (!f) return -1;
Буфер тоже делаем RAII-шным — и теперь любой ранний
return безопасен:auto buf = std::make_unique<char[]>(1024);
if (something_bad()) return -1;
return 0;
RAII убирает goto cleanup и ручные освобождения: добавляешь новые ветки и выходы — и не думаешь, где что закрыть.Please open Telegram to view this post
VIEW IN TELEGRAM
❤14👍7🔥6
This media is not supported in your browser
VIEW IN TELEGRAM
Если обычные видеоуроки не заходят и хочется больше практики — Scrimba решает эту задачу. Здесь обучение построено так, что ты не просто смотришь объяснение, а сразу работаешь с кодом прямо в уроке: меняешь примеры, запускаешь и смотришь результат. На платформе есть множество языков и технологий.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤9🔥7
std::move не перемещает, а “разрешает перемещать”!
Частая ошибка — думать, что
На самом деле
Реальное перемещение происходит только там, где вызывается:
• move-конструктор
• move-оператор присваивания
• или перегрузка функции, принимающая rvalue (
Поэтому строка:
не “переместила” ничего магически — она просто создала
Перемещение гарантированно случается, когда ты строишь/присваиваешь объект из rvalue:
Важный практический вывод: после
🔥 Итог: ставь
📣 C++ Ready | #совет
Частая ошибка — думать, что
std::move(x) сам переносит ресурсы и “обнуляет” объект.На самом деле
std::move — это просто приведение к rvalue-ссылке: он говорит компилятору “считай, что этот объект можно перемещать”.Реальное перемещение происходит только там, где вызывается:
• move-конструктор
• move-оператор присваивания
• или перегрузка функции, принимающая rvalue (
T&&)Поэтому строка:
auto b = std::move(a);
не “переместила” ничего магически — она просто создала
b (а вот *каким образом* — копированием или перемещением — зависит от типа и контекста).Перемещение гарантированно случается, когда ты строишь/присваиваешь объект из rvalue:
std::string c = std::move(b); // move-конструктор
Важный практический вывод: после
std::move(x) объект x остаётся валидным, но его состояние не определено (его можно переиспользовать только через присваивание или безопасные операции типа clear()/empty()/size() — в зависимости от типа).std::move там, где ты вроде бы “отдаёшь” объект (в контейнер, в return, в конструктор/присваивание) — это не перенос, а явный сигнал: “дальше мне это значение не нужно”.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14👍7❤5
This media is not supported in your browser
VIEW IN TELEGRAM
Сайт с огромной коллекцией задач по программированию, от самых простых до тех, что реально заставят подумать.
Учиться можно на абсолютно любом языке. Отличный способ подтянуть логику, научиться писать аккуратный код и подготовиться к собесам.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤15👍8🔥8😁1
Поставлю себе цель: набрать более 25 тысяч подписчиков. Желаю каждому выполнить планы.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍31🔥11❤8😁2
Как min/max из windows.h ломают std::min/std::max?
На Windows легко словить странную ошибку компиляции в невинном месте:
Причина:
Правильный вариант — отключить эти макросы до подключения Windows-заголовков:
Если
И есть аварийный трюк, если нельзя менять include-порядок: обернуть имя в скобки — тогда макрос не сработает:
🔥 Итог: на Windows держи правило — `NOMINMAX` включён всегда, иначе стандартная библиотека будет ломаться в самых неожиданных местах.
📣 C++ Ready | #практика
На Windows легко словить странную ошибку компиляции в невинном месте:
#include <windows.h>
#include <algorithm>
auto m = std::min(a, b);
Причина:
windows.h (и некоторые связанные хедеры) могут определить макросы min/max. Тогда std::min(a, b) превращается в макро-подстановку, и начинает “ехать” всё, что выглядит как вызов функции.Правильный вариант — отключить эти макросы до подключения Windows-заголовков:
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <algorithm>
auto m = std::min(a, b);
Если
windows.h уже подключили (или его подтянула зависимость), можно убрать макросы точечно:#include <windows.h>
#undef min
#undef max
#include <algorithm>
И есть аварийный трюк, если нельзя менять include-порядок: обернуть имя в скобки — тогда макрос не сработает:
auto m = (std::min)(a, b);
Please open Telegram to view this post
VIEW IN TELEGRAM
👍22🤝9❤8
Как перестать возвращать -1 и “магические значения”, которые ломают логику?
Классика: функция “ищет/парсит/получает” и возвращает
Вместо этого возвращай
Пример:
• сигнатура:
• использование через проверку:
• самый удобный паттерн:
В итоге интерфейс становится честным, проверки становятся естественными, а “забыл обработать” видно сразу при чтении кода.
🔥 Не забудь:
📣 C++ Ready | #совет
Классика: функция “ищет/парсит/получает” и возвращает
int, а “не найдено” кодируют как -1 или 0. Проблема в том, что это не видно из сигнатуры, и рано или поздно кто-то забывает проверить значение — баг готов.Вместо этого возвращай
std::optional<T> — тип прямо говорит: “значения может не быть”.Пример:
• сигнатура:
std::optional<int> find_user_id(std::string_view name);• использование через проверку:
if (id1) use_user(*id1);• самый удобный паттерн:
if (auto id = find_user_id("morpheus")) use_user(*id);В итоге интерфейс становится честным, проверки становятся естественными, а “забыл обработать” видно сразу при чтении кода.
*id безопасно только после проверки, а для значения по умолчанию есть id.value_or(-1) (если прям нужно).Please open Telegram to view this post
VIEW IN TELEGRAM
❤20🔥6👍4🤝1
This media is not supported in your browser
VIEW IN TELEGRAM
Бесплатный веб-сервис для обучения и практики программирования: собирает задачи от вводных («Hello World» и основ синтаксиса) до алгоритмов и структур данных, а также задачи из реальных программных соревнований и курсов.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤5🔥4
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥16❤8👍4🤝2
На картинке — аккуратная “Span Cheat Sheet”, которая показывает, как работать с
std::span: лёгким и не владеющим памятью представлением непрерывной последовательности объектов. Показано, как создавать span из вектора или массива, как получать элементы, размер, поддиапазоны и почему span особенно удобен в параметрах функций.
Сохрани, чтобы больше не путаться с указателями и массивами
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17🔥7❤4
Как добавить “поле-настройку” в struct и не раздувать память?
Иногда хочется хранить рядом с данными “маркер/политику/стратегию”, но сам тип пустой. В обычном виде даже пустой член класса вроде
В C++20 есть атрибут
Сравни:
• было:
• стало:
Это видно через измерение размера:
Во многих ABI
🔥 Это оптимизация, а не гарантия. И ещё нюанс: у поля вроде
📣 C++ Ready | #совет
Иногда хочется хранить рядом с данными “маркер/политику/стратегию”, но сам тип пустой. В обычном виде даже пустой член класса вроде
Tag tag; может занимать место — компилятор часто обязан обеспечивать уникальность адресов и соблюдать выравнивание.В C++20 есть атрибут
[[no_unique_address]], который разрешает компилятору не выделять отдельную память под пустой член.Сравни:
• было:
struct A { int value; Tag tag; };• стало:
struct B { int value; [[no_unique_address]] Tag tag; };Это видно через измерение размера:
std::cout << sizeof(A);
std::cout << sizeof(B);
Во многих ABI
B будет меньше (или не больше), потому что Tag может “слиться” с уже существующим адресом внутри объекта.[[no_unique_address]] Tag tag; адрес &obj.tag может совпасть с &obj, так что не полагайся на уникальность адреса.Please open Telegram to view this post
VIEW IN TELEGRAM
❤9👍6🔥4🤝1
Поговорим про std::ssize: размер контейнера со знаком — без кастов и ловушек size_t!
Полезно в обратных циклах и сравнениях индексов, где
Почему так? Потому что
Классическая проблема выглядит так:
Условие
и проверки
🔥
📣 C++ Ready | #практика
Полезно в обратных циклах и сравнениях индексов, где
size_t легко ломает логику.std::vector<int> v{10, 20, 30};
for (int i = std::ssize(v) - 1; i >= 0; --i) {
std::cout << v[i] << ' ';
}Почему так? Потому что
v.size() возвращает size_t, а это беззнаковый тип. Из-за этого даже простые проверки и “обратные” циклы легко превращаются в ловушки: компилятор ругается на сравнение знакового и беззнакового, а иногда логика вообще ломается.Классическая проблема выглядит так:
for (size_t i = v.size() - 1; i >= 0; --i) {
std::cout << v[i] << ' ';
}Условие
i >= 0 для size_t всегда истинно, и цикл может уйти в бесконечность, потому что i никогда не станет “меньше нуля”, он просто переполнится.std::ssize(v) решает это правильно и без кастов. Он возвращает размер контейнера, но уже в знаковом типе, поэтому выражения вродеstd::ssize(v) - 1
и проверки
i >= 0 ведут себя ожидаемо, а предупреждения компилятора исчезают.std::ssize — маленькая привычка, которая убирает целый класс багов вокруг size_t, особенно в циклах и сравнениях индексов.Please open Telegram to view this post
VIEW IN TELEGRAM
❤20👍7🔥4
В этой статье:
• Поймёшь, почему «один клиент — один поток» плохо • Соберёшь ThreadPool: очередь задач, воркеры, синхронизация без лишних аллокаций• Построишь конечный автомат: состояния, хендлеры, переходы для HTTP-клиента 🔊 Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍3🔥3
Когда в функции несколько
return, а ещё возможны исключения — легко забыть закрыть файл, откатить флаг или завершить “временный режим”. scope_exit решает это просто: задаёшь действие один раз — и оно гарантированно выполнится при выходе из блока.В этом посте:
• Поймём, как работает scope_exit и почему он похож на defer/finally;
• Разберём базовый шаблон “ресурс + уборка рядом” на коротких примерах;
• Посмотрим реальный кейс: мини-профайлер и автоматический rollback.
Освоив
scope_exit, вы сможете делать cleanup в одном месте, не размазывая его по коду, и спокойно добавлять новые выходы из функции без риска “забыть убрать”.Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥18❤9👍6