Заменить значение и забрать старое — std::exchange!
Иногда нужно “переключить” переменную на новое значение и при этом сохранить старое: например, сбросить флаг, обнулить указатель, забрать ресурс у объекта. Вместо двух строк и временной переменной есть удобная функция
Сначала типичный “ручной” вариант:
Теперь то же самое одной строкой:
Частый практический кейс — “забрать” указатель и обнулить его, чтобы избежать повторного использования:
И ещё один популярный пример — move-конструктор, где нужно забрать ресурс и оставить исходный объект в безопасном состоянии:
🔥
📣 C++ Ready | #практика
Иногда нужно “переключить” переменную на новое значение и при этом сохранить старое: например, сбросить флаг, обнулить указатель, забрать ресурс у объекта. Вместо двух строк и временной переменной есть удобная функция
std::exchange.Сначала типичный “ручной” вариант:
int old = value;
value = 0;
use(old);
Теперь то же самое одной строкой:
std::exchange возвращает старое значение и записывает новое:int old = std::exchange(value, 0);
use(old);
Частый практический кейс — “забрать” указатель и обнулить его, чтобы избежать повторного использования:
T* take_ptr(T*& p) {
return std::exchange(p, nullptr);
}И ещё один популярный пример — move-конструктор, где нужно забрать ресурс и оставить исходный объект в безопасном состоянии:
struct Buffer {
int* data = nullptr;
Buffer(Buffer&& other) noexcept
: data(std::exchange(other.data, nullptr)) {}
};std::exchange делает намерение явным: “забрать старое, поставить новое” — меньше кода, меньше шансов забыть сбросить состояние.Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🔥11❤6
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🤝32🔥18❤5👍1
В этой статье:
• Понять, как RAII помогает строить архитектуру и инварианты• Увидеть примеры управления разными ресурсами (не только памятью)• Научиться делать код устойчивым к исключениям и “утечкам ответственности”🔊 Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍6❤5😁1
Безопасный “побитовый каст” без UB — std::bit_cast!
Иногда нужно посмотреть на биты значения: получить IEEE754-представление
Многие делают это через
Сначала типичный “ручной” (опасный) вариант:
Теперь то же самое правильно одной строкой:
Частый практический кейс — быстро проверить знак/экспоненту
Ещё один популярный пример — получить “сырой”
И обратная операция — восстановить значение из битов (тоже безопасно, если размер совпадает):
🔥
📣 C++ Ready | #практика
Иногда нужно посмотреть на биты значения: получить IEEE754-представление
float, собрать/разобрать сетевой пакет, сделать хеш по битам, интерпретировать double как uint64_t.Многие делают это через
reinterpret_cast или “указательную магию” — и ловят UB из-за strict aliasing и выравнивания.Сначала типичный “ручной” (опасный) вариант:
float f = 1.0f;
auto u = *reinterpret_cast<std::uint32_t*>(&f); // UB!
use(u);
Теперь то же самое правильно одной строкой:
std::bit_cast (C++20) копирует биты между типами одинакового размера:float f = 1.0f;
auto u = std::bit_cast<std::uint32_t>(f);
use(u);
Частый практический кейс — быстро проверить знак/экспоненту
float без математики:bool is_negative(float x) {
auto bits = std::bit_cast<std::uint32_t>(x);
return (bits >> 31) != 0;
}Ещё один популярный пример — получить “сырой”
uint64_t из double для стабильного сравнения/логирования битов:std::uint64_t raw_bits(double d) {
return std::bit_cast<std::uint64_t>(d);
}И обратная операция — восстановить значение из битов (тоже безопасно, если размер совпадает):
double from_bits(std::uint64_t bits) {
return std::bit_cast<double>(bits);
}std::bit_cast делает намерение явным: “мне нужны биты, а не преобразование значения” — без reinterpret_cast, без строгого алиасинга, меньше шансов поймать UB.Please open Telegram to view this post
VIEW IN TELEGRAM
❤14🔥9👍5
This media is not supported in your browser
VIEW IN TELEGRAM
Бесплатный “one-stop shop” для подготовки к USACO: структурированные разделы от Intro и Bronze до Platinum/Advanced, где в каждом модуле есть объяснение темы, советы по реализации, подборка задач для практики и разборы/редакции
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12👍8🔥5🤝1
Почему std::move не “перемещает”?
Многие думают, что
Проблема начинается, когда после
После перемещения объект обязан оставаться валидным, но его состояние становится неопределённым (у
Правильное правило простое:
•
• после этого
🔥 Итог:
📣 C++ Ready | #совет
Многие думают, что
std::move(x) физически “переносит” данные и очищает x. Но std::move ничего не двигает — это всего лишь приведение к rvalue-ссылке, то есть сигнал компилятору: “этот объект можно перемещать”.Проблема начинается, когда после
std::move мы продолжаем пользоваться объектом как раньше:std::string s = "hello";
std::string a = std::move(s);
std::cout << s; // ❌ логическая ошибка
После перемещения объект обязан оставаться валидным, но его состояние становится неопределённым (у
std::string часто будет пусто, но полагаться на это нельзя). Поэтому любые “проверю что осталось” и “ещё раз использую” — источник багов.Правильное правило простое:
•
std::move(x) = “я отказываюсь от значения x”• после этого
x можно только: присвоить новое значение / уничтожить / вызвать безопасные операции, не зависящие от содержимого.std::move — не перенос, а разрешение на перенос. Если ты пишешь std::move, считай, что “объект дальше мне не нужен” — так код становится предсказуемым и без скрытых сюрпризов.Please open Telegram to view this post
VIEW IN TELEGRAM
👍22🔥9❤6🤝2
Антипаттерн: # define DEBUG 1 vs NDEBUG
Часто для отладочного кода делают что-то вроде
Работает? Да.
Надёжно? Не всегда.
Проблема в том, что такой
В стандарте C++ уже есть специальный макрос для этого —
Именно на него завязаны
Плохой вариант:
Если забыть убрать
Правильный вариант — ориентироваться на
Теперь всё согласовано:
• debug-сборка → логирование и проверки есть
• release (
Точно так же стоит писать и свои отладочные макросы:
🔥 Использование
📣 C++ Ready | #практика
Часто для отладочного кода делают что-то вроде
#define DEBUG 1 и оборачивают всё в #ifdef DEBUG.Работает? Да.
Надёжно? Не всегда.
Проблема в том, что такой
DEBUG — не стандартный, и он легко расходится с реальным типом сборки. В итоге можно случайно оставить отладочный код в релизе.В стандарте C++ уже есть специальный макрос для этого —
NDEBUG. Если NDEBUG не определён, значит это debug-сборка. Если NDEBUG определён, значит релиз.Именно на него завязаны
assert и многие стандартные проверки.Плохой вариант:
#define DEBUG 1
void foo(int x) {
#ifdef DEBUG
std::cout << "x = " << x << "\n";
#endif
work(x);
}
Если забыть убрать
DEBUG или определить его не там — отладочный код спокойно уедет в прод.Правильный вариант — ориентироваться на
NDEBUG:void foo(int x) {
#ifndef NDEBUG
std::cerr << "x = " << x << "\n";
#endif
work(x);
}Теперь всё согласовано:
• debug-сборка → логирование и проверки есть
• release (
-DNDEBUG) → код полностью вырезается компиляторомТочно так же стоит писать и свои отладочные макросы:
#ifndef NDEBUG
#define DBG_LOG(msg) std::cerr << msg << "\n"
#else
#define DBG_LOG(msg) ((void)0)
#endif
NDEBUG — это не мелочь, а дисциплина сборки: отладочный код никогда не попадёт в релиз случайно, и всё поведение будет согласовано со стандартными механизмами C++.Please open Telegram to view this post
VIEW IN TELEGRAM
❤22👍12🔥7
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥22🤝11❤6👍2
На картинке — компактная шпаргалка по скобкам в Bash:
(), {}, $(), [], [[ ]] — что они делают и чем отличаются (подстановка команд, группировка в текущем shell/подоболочке, массивы, brace expansion, параметрическое расширение, арифметика и проверки условий).Сохрани, чтобы не путаться в синтаксисе и быстрее писать скрипты!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤13🔥9👍7👎1
push_back vs emplace_back: когда что использовать?
Обе функции добавляют элемент в контейнер, но делают это по-разному.
Это полезно, когда:
• элемент создаётся “на месте” из нескольких параметров;
• хочется избежать лишнего временного объекта (особенно для тяжёлых типов).
🔥 Важный нюанс: в современном C++ разница часто не драматическая (компилятор умеет оптимизировать временные объекты), так что выбирать стоит по читаемости.
📣 C++ Ready | #совет
Обе функции добавляют элемент в контейнер, но делают это по-разному.
push_back(...) добавляет уже готовый объект. Если передать lvalue — будет копия, если rvalue — будет перемещение:v.push_back(s); // копия
v.push_back(std::move(s)); // move
emplace_back(...) не требует готового объекта — он передаёт аргументы в конструктор элемента и создаёт его *сразу внутри* контейнера:v.emplace_back(5, 'a'); // string("aaaaa") прямо в vectorЭто полезно, когда:
• элемент создаётся “на месте” из нескольких параметров;
• хочется избежать лишнего временного объекта (особенно для тяжёлых типов).
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15❤10👍7
В этой статье:
• Поймёшь, какие микроизменения действительно влияют на скорость• Увидишь примеры про типы, порядок полей, преобразования и “дорогие” операции• Заберёшь идеи про циклы, ветвления и вызовы, чтобы вычищать лишнее без фанатизма🔊 Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤11🔥5👍4😁2
Знали что, многие допускают ошибку при использовании оператора delete?
Сегодня как раз ее разберем. Распространенную ошибку в C++ при работе с операторами delete и delete[], она может привести к сбоям программы и утечкам памяти:
Операторы
Для освобождения памяти, выделенной с помощью
Правильное использование delete[]:
Другой распространенный случай — удаление одной и той же памяти дважды. Это может привести к неопределенному поведению:
Установить указатель в nullptr
После того как память освобождена, всегда обнуляйте указатель:
🔥 Правильное использование этих операторов — ключ к предотвращению утечек памяти и ошибок в управлении памятью.
Всегда следите за тем, какой тип памяти вы освобождаете, и избегайте двойного удаления.
📣 C++ Ready | #практика
Сегодня как раз ее разберем. Распространенную ошибку в C++ при работе с операторами delete и delete[], она может привести к сбоям программы и утечкам памяти:
Операторы
delete и delete[] предназначены для освобождения памяти, выделенной с помощью new и new[] соответственно. Часто можно столкнуться с ошибками, если перепутать эти операторы:int* ptr = new int[5]; // Выделили память для массива
delete ptr; // Ошибка! Использован неправильный оператор
Для освобождения памяти, выделенной с помощью
new[], нужно использовать delete[].Правильное использование delete[]:
int* ptr = new int[5]; // Выделили память для массива
delete[] ptr; // Правильно освобождаем память
Другой распространенный случай — удаление одной и той же памяти дважды. Это может привести к неопределенному поведению:
int* ptr = new int[5];
delete[] ptr; // Освободили память
delete[] ptr; // Ошибка: память уже освобождена
Установить указатель в nullptr
После того как память освобождена, всегда обнуляйте указатель:
int* ptr = new int[5];
delete[] ptr;
ptr = nullptr; // Указатель больше не указывает на освобожденную память
🔥 Правильное использование этих операторов — ключ к предотвращению утечек памяти и ошибок в управлении памятью.
Всегда следите за тем, какой тип памяти вы освобождаете, и избегайте двойного удаления.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🔥13❤9