Когда в функции несколько
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
Почему после placement new “старый указатель” может начать врать?
В низкоуровневом коде иногда создают объект прямо в заранее выделенной памяти:
Проблема начинается, когда ты вручную заканчиваешь жизнь объекта и создаёшь новый в той же памяти:
и сразу после этого:
Логически кажется: “адрес тот же, значит
После этого чтение через
🔥 Если у тебя обычный прикладной код без ручного управления временем жизни объектов — скорее всего он тебе никогда не понадобится.
📣 C++ Ready | #совет
В низкоуровневом коде иногда создают объект прямо в заранее выделенной памяти:
S* p = new (storage) S(1);. Это placement new — объект живёт в буфере, а не в куче.Проблема начинается, когда ты вручную заканчиваешь жизнь объекта и создаёшь новый в той же памяти:
p->~S();
и сразу после этого:
new (storage) S(2);
Логически кажется: “адрес тот же, значит
p снова указывает на актуальный объект”. Но по правилам C++ это уже другой объект с новой lifetime, и компилятор имеет право оптимизировать так, будто через p ты всё ещё обращаешься к старому (особенно в сложных сценариях внутри шаблонов/контейнеров).std::launder (C++17) — это способ “перепривязать” указатель к новому объекту и дать компилятору понять, что в этой памяти теперь другая сущность:S* q = std::launder(p);
После этого чтение через
q->x считается корректным и не ломается оптимизациями.Please open Telegram to view this post
VIEW IN TELEGRAM
❤11👍8🔥4
This media is not supported in your browser
VIEW IN TELEGRAM
Официальная документация Extra Clang Tools: clang-tidy помогает находить и исправлять типичные ошибки в коде — от нарушений стиля и misuse интерфейсов до багов, которые можно вывести статическим анализом.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10❤4👍4
std::byte: честные “сырые байты” вместо char/uint8_t!
Он помогает не путать бинарные данные с текстом и не ловить случайную арифметику над “байтами”.
Часто под байты берут
Проблема в том, что такие типы легко начинают жить как “маленькие числа” или “символы”: кто-то делает
В C++17 для этого есть
С ним нельзя случайно делать арифметику, зато можно явно делать побитовые операции.
Как хранить и передавать байты:
Если нужно получить число — делай это явно:
🔥
📣 C++ Ready | #практика
Он помогает не путать бинарные данные с текстом и не ловить случайную арифметику над “байтами”.
Часто под байты берут
uint8_t или char:void send(const std::vector<uint8_t>& data);
int main() {
std::vector<uint8_t> buf = {0x48, 0x65, 0x6C, 0x6C, 0x6F};
send(buf);
}
Проблема в том, что такие типы легко начинают жить как “маленькие числа” или “символы”: кто-то делает
buf[i] + 1, кто-то печатает как текст, и смысл буфера расползается.В C++17 для этого есть
std::byte — отдельный тип именно для бинарщины.С ним нельзя случайно делать арифметику, зато можно явно делать побитовые операции.
Как хранить и передавать байты:
void send(std::span<const std::byte> data);
int main() {
std::vector<std::byte> buf = {
std::byte{0x48}, std::byte{0x65}, std::byte{0x6C},
std::byte{0x6C}, std::byte{0x6F}
};
send(buf);
}
Если нужно получить число — делай это явно:
#include <bit>
std::byte b{0xFF};
int x = std::to_integer<int>(b);
std::byte делает работу с бинарными данными ясной: меньше неявных превращений, меньше путаницы “текст vs байты”, больше контроля.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥17❤5👍4👎1
👍11❤5🔥3
Например,
192.168.x.x — для домашней сети, а 10.x.x.x — для крупных корпоративных систем. CIDR (/24, /16, /8) — помогает точно задать размер подсети и количество хостов.На картинке — всё, что нужно знать про IP: диапазоны, маски, специальные адреса, публичные DNS и основы IPv6.
Сохрани, чтобы не забыть!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤27👍9🔥8
Почему vector{10, 1} и vector(10, 1) — это разные контейнеры?
На вид разница “всего лишь в скобках”, но смысл радикально меняется.
Круглые скобки выбирают конструктор “размер + значение”, то есть создают 10 элементов со значением 1:
А фигурные скобки в первую очередь пытаются попасть в конструктор с
Это ловушка потому, что
🔥 Если ты хочешь “N одинаковых элементов” — используй
📣 C++ Ready | #совет
На вид разница “всего лишь в скобках”, но смысл радикально меняется.
Круглые скобки выбирают конструктор “размер + значение”, то есть создают 10 элементов со значением 1:
std::vector<int> a(10, 1);
А фигурные скобки в первую очередь пытаются попасть в конструктор с
std::initializer_list, который трактует аргументы как список элементов. Поэтому тут создаётся вектор из двух элементов: 10 и 1:std::vector<int> b{10, 1};Это ловушка потому, что
initializer_list при наличии часто “побеждает” другие перегрузки — и ты получаешь не тот контейнер, который ожидал.(n, value). Если хочешь “конкретные элементы” — используй {...}Please open Telegram to view this post
VIEW IN TELEGRAM
🔥21👍9❤8🤝1
На картинке — компактная памятка по std::string_view: что это лёгкий, не владеющий памятью и read-only “вид” на строку/массив символов, как его удобно передавать в функции без лишних копий, и какие методы чаще всего используются
Отдельно подсвечены важные моменты: string_view отлично подходит для подстрок и диапазонов без копирования, но нужно следить за временем жизни исходной строки — view может “пережить” данные и стать висячим.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤13👍5🔥5
Программа показывает последовательность цифр по одной, быстро стирает экран и просит ввести всё в том же порядке, превращая обычный запуск в маленький тренажёр внимания.
В этой задаче узнаешь:
• Как генерировать случайную последовательность и хранить её в std::vector;
• Как паузы через std::chrono и sleep_for создают “напряжение” и делают игру честной;
• Как быстро проверить результат, сравнивая ответы по позициям.
Пара простых циклов — и у тебя готова игра, которую легко прокачать уровнями: после победы увеличивать длину и собирать свой личный рекорд.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥24❤5👍4😁1🤝1
Media is too big
VIEW IN TELEGRAM
CppDeveloperRoadmap — репозиторий, в котором собраны книги, идеи для пэт-проектов и полезные ресурсы для изучения. Все материалы разбиты на уровни junior, middle и senior.
📣 C++ Ready | #репозиторий
Оставляю ссылочку: Github📱
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12❤5👍3
std::span + subspan(): “нарезай” буфер на части без копий и лишних new!
Это особенно удобно для протоколов и парсинга, где есть заголовок, тело и хвост.
Часто делают так: копируют куски в новые контейнеры.
Проблема в том, что это лишние аллокации и копирование: на каждом разрезе создаётся новый буфер, а в горячем коде парсинга это быстро становится дорогим.
Вместо копий — просто виды на исходный буфер:
Использование выглядит так же просто:
Важно помнить:
🔥
📣 C++ Ready | #практика
Это особенно удобно для протоколов и парсинга, где есть заголовок, тело и хвост.
Часто делают так: копируют куски в новые контейнеры.
std::vector<uint8_t> header(const std::vector<uint8_t>& buf, size_t n) {
return {buf.begin(), buf.begin() + n};
}
std::vector<uint8_t> payload(const std::vector<uint8_t>& buf, size_t n) {
return {buf.begin() + n, buf.end()};
}Проблема в том, что это лишние аллокации и копирование: на каждом разрезе создаётся новый буфер, а в горячем коде парсинга это быстро становится дорогим.
std::span не владеет памятью, зато позволяет сделать “вид” на нужный участок, и subspan() как раз возвращает такой срез.Вместо копий — просто виды на исходный буфер:
struct PacketView {
std::span<const std::uint8_t> hdr;
std::span<const std::uint8_t> body;
};
PacketView split(std::span<const std::uint8_t> buf, std::size_t n) {
return {
buf.subspan(0, n),
buf.subspan(n)
};
}Использование выглядит так же просто:
int main() {
std::vector<std::uint8_t> buf = {1,2,3,4,5,6,7,8};
auto p = split(buf, 3);
// p.hdr -> {1,2,3}
// p.body -> {4,5,6,7,8}
}Важно помнить:
span живёт только пока живут исходные данные, поэтому нельзя возвращать span на временный объект или локальный буфер, который скоро уничтожится.subspan() — это “срез без копии”: идеально для парсинга и разборки буферов, когда нужно быстро выделять части и не плодить новые вектора.Please open Telegram to view this post
VIEW IN TELEGRAM
❤15🔥8👍5
В этой статье:
• Понять, зачем в сервере столько уровней шаблонов вообще• Увидеть, как через типы собирается конфигурация API сервера• Разобраться, как шаблоны уменьшают бойлерплейт без рантайма лишнего🔊 Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7❤3👍3😁1