tgindex

C++ geek

описание

Учим C/C++ на примерах

3 557
подписчиков
Охват к подписчикам
18,3%
ERR
Реакции к просмотрам
0,77%
322 на 38 постов
Пересылки к просмотрам
0,96%
405
Постов в день
0,3
всего 38

Где отзываются чаще

доля реакций к просмотрам
  • 07:21Зачем [[nodiscard]] и когда он реально спасает? Сколько раз вы видели: функция возвращает значение, а кто-то его тупо игнорирует. А потом баги из ниоткуда. В C++17 завезли [[nodiscard]], и это реально полезная штука. [[nodiscard]] int compute() { return 42; } int main() { compute(); // warning: ignoring return value } Компилятор предупреждает: "эй, ты вызвал, но не используешь результат". Где это особенно важно: - Функции, которые возвращают ошибки (std::error_code, std::optional, bool успеха операции); - Функции, где пропуск результата ломает логику (например, RAII-объекты, токены отмены, хендлы). Можно навесить [[nodiscard]] и на типы (C++20): struct [[nodiscard]] Result { bool ok; }; Result foo(); foo(); // warning! Вместо неочевидных багов — раннее предупреждение компилятора. ⚡ Лайфхак: Если компилятор слишком шумит — можно сделать [[nodiscard("объяснение")]] (C++20), чтобы явно подсказать, зачем это. Вывод: ставьте [[nodiscard]] на всё, что нельзя безнаказанно проигнорировать. Это дешёвая страховка от глупых багов. 📲 Мы в MAX ➡️ @cpp_geek1,69%
  • 3 авг.Зачем std::move от std::string в emplace_back? Кажется, emplace_back(std::move(str)) — избыточно: ведь emplace_back сам конструирует объект на месте. Но с std::string и другими перемещаемыми типами всё не так просто. Пример: std::vector<std::string> vec; std::string str = "hello"; vec.emplace_back(str); // копия vec.emplace_back(std::move(str)); // перемещение Почему? Потому что: > emplace_back вызывает конструктор std::string(const std::string&), если аргумент — const std::string&, > и std::string(std::string&&), если аргумент — std::string&&. А str — это lvalue. Даже внутри emplace_back. Без std::move, ты просишь вектор скопировать строку. С std::move — переместить. Это относится ко всем emplace_*, когда ты передаёшь существующий объект. emplace_back("hello") — другое дело: тут создаётся временный const char*, и он уже используется для конструктора string. 👉 Правило: если аргумент — перемещаемый объект (например, std::string, std::vector), и ты передаёшь его в emplace_*, не забывай std::move. Иначе будет лишняя копия. 📲 Мы в MAX ➡️ @cpp_geek1,53%
  • 4 апр.без подписи1,46%
  • 5 авг.Move-семантика: где можно ловко сэкономить Многие знают про std::move, но не всегда используют его там, где это реально ускоряет код. Простой пример — возврат локального объекта из функции: #include <string> std::string make_string() { std::string s = "Hello"; return s; // RVO или move } С C++17 тут почти всегда RVO (Return Value Optimization). Но если RVO невозможен (например, возвращаем тернарный оператор), компилятор применит move: std::string make_string(bool flag) { std::string a = "foo", b = "bar"; return flag ? a : b; // тут будет move } А вот так можно подсказать компилятору явно: return std::move(flag ? a : b); Но осторожно: не делайте std::move для локальной переменной в простом return — это может сломать RVO и привести к лишнему перемещению. Ещё полезно помнить: move не всегда бесплатный. Например, для std::vector он копирует указатель и размер, но не элементы. Для std::string — зависит от Small String Optimization: короткие строки перемещаются как копия. Вывод: используйте std::move там, где явно хотите отдать объект, а не копировать. Но не злоупотребляйте им: компилятор с C++17 сам неплохо справляется. 📲 Мы в MAX ➡️ @cpp_geek1,37%
  • 23 июл.🧵 Сегодня покажу вам простой способ логгировать вызовы функций в C++ — пригодится для отладки и анализа кода. Часто бывает нужно понять, какие функции вызываются, в каком порядке и с какими параметрами. Вручную вставлять std::cout — неудобно. Вместо этого используем RAII-макрос с выводом в консоль: #include <iostream> #include <string> struct FunctionLogger { std::string func_name; FunctionLogger(const std::string& name) : func_name(name) { std::cout << ">> Entering: " << func_name << '\n'; } ~FunctionLogger() { std::cout << "<< Exiting: " << func_name << '\n'; } }; #define LOG_FUNCTION() FunctionLogger logger(__FUNCTION__) Теперь в любой функции достаточно просто написать LOG_FUNCTION();, и вы получите автоматический лог при входе и выходе: void do_work() { LOG_FUNCTION(); // Работаем... } Это особенно удобно в больших проектах, когда нужно быстро локализовать ошибку или понять структуру вызовов. Можно доработать: лог в файл, потокобезопасность, включение по флагу компиляции и т.д. 📲 Мы в MAX ➡️ @cpp_geek1,35%
  • 25 июл.🚀 Подборка полезных IT каналов в Max Системное администрирование, DevOps 📌 https://max.ru/i_odmin Все для системного администратора https://max.ru/bash_srv Bash Советы https://max.ru/sysadminof Книги для админов, полезные материалы https://max.ru/i_odmin_book Библиотека Системного Администратора https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др. https://max.ru/tipsysdmin Типичный Сисадмин https://max.ru/channel_win_sysadmin Системный Администратор Windows https://max.ru/channel_linux_admin Linux: Системный администратор https://max.ru/channel_linuxmod Linux https://max.ru/channel_i_linux Системный администратор https://max.ru/channel_devopslib DevOps, SRE, Sysadmin https://max.ru/channel_devops_star DevOps Star (Звезда Девопса) Excel лайфхак 📌 https://t.me/Excel_lifehack Excel лайфхак Английский с нуля 🇬🇧 https://max.ru/UchuEnglish 1C разработка 📌 https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С https://max.ru/channel_DevLab1C 1С:Предприятие 8 Программирование C++📌 https://max.ru/cpp_lib Библиотека C/C++ разработчика https://max.ru/channel_cpp_geek C++ geek Программирование Go📌 https://max.ru/golang_lib Библиотека Go (Golang) разработчика Программирование React📌 https://max.ru/react_lib React Программирование Rust📌 https://max.ru/channel_rust_lib Программирование Python 📌 https://max.ru/python_of Python академия. https://max.ru/BookPython Библиотека Python разработчика Java разработка 📌 https://max.ru/bookjava Библиотека Java разработчика https://max.ru/channel_java_geek Java Geek GitHub Сообщество 📌 https://max.ru/githublib Интересное из GitHub Базы данных (Data Base) 📌 https://max.ru/database_info Все про базы данных Фронтенд разработка 📌 https://max.ru/frontend_1 Подборки для frontend разработчиков Библиотеки 📌 https://max.ru/programmist_of Книги по программированию https://max.ru/proglb Библиотека программиста https://max.ru/bfbook Книги для программистов Программирование 📌 https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT https://max.ru/php_lib Библиотека PHP программиста 👨🏼‍💻👩‍💻 Шутки программистов 📌 https://max.ru/itumor Шутки программистов Защита, взлом, безопасность 📌 https://max.ru/thehaking Канал о кибербезопасности https://max.ru/xakkep_1 Хакер Free Книги, статьи для дизайнеров 📌 https://max.ru/odesigners Статьи, книги для дизайнеров Математика 📌 https://max.ru/Pomatematike Канал по математике https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике Вакансии в IT📌 https://max.ru/progjob https://max.ru/channel_rabotait Мир технологий 📌 https://max.ru/mir_teh Канал для любознательных Бонус 📌 https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга https://max.ru/mockva_life Свежие новости Москвы https://max.ru/piterspb Питер Новости: Санкт-Петербург / СПБ / ДТП1,33%
  • 14 февр.без подписи1,26%
  • 10 авг.RAII — твой лучший друг (и почему не стоит бояться умных указателей) Старый добрый new / delete — это классика, но и источник утечек, крашей и боли. В современном C++ ручное управление памятью почти всегда антипаттерн. Решение — RAII (Resource Acquisition Is Initialization): ресурсы живут ровно столько, сколько объект, который ими владеет. Ушёл объект из области видимости — ресурс освободился. Пример с умными указателями: #include <memory> #include <iostream> struct Foo { Foo() { std::cout << "Init\n"; } ~Foo() { std::cout << "Destroy\n"; } }; void bar() { std::unique_ptr<Foo> p = std::make_unique<Foo>(); // RAII // делаем что-то } // тут автоматически вызовется ~Foo() Что важно знать: * std::unique_ptr — владение в единственном числе, идеально для большинства случаев. * std::shared_ptr — разделённое владение (но дороже по производительности). * Никогда не делай new без обёртки — почти всегда лучше std::make_unique или std::make_shared. RAII работает не только для памяти: файлы, мьютексы, сокеты — всё. Достаточно обернуть ресурс в класс с деструктором. Профит: меньше багов, меньше утечек, чище код. 📲 Мы в MAX ➡️ @cpp_geek1,22%
  • 31 июл.Что такое ADL и как она может вас подставить Argument-Dependent Lookup (ADL) — это фича, которая может как упростить код, так и привести к сюрпризам. Представьте: namespace math { struct Vec {}; void print(const Vec&) { std::cout << "Vec\n"; } } void foo() { math::Vec v; print(v); // компилятор найдёт math::print через ADL! } Хотя print не в текущем скоупе, компилятор ищет её в namespace аргумента v, т.е. math. Это и есть ADL. Звучит удобно. Но... ⚠️ Опасность: namespace lib { struct Widget {}; void process(const Widget&) { std::cout << "lib::process\n"; } } void process(int) { std::cout << "global::process\n"; } void bar() { lib::Widget w; process(w); // вызовет lib::process, а не global::process! } Такой вызов может внезапно потянуть неожиданные функции, особенно в generic-коде (например, в шаблонах), где ADL может найти «лишнее». ✅ Best practices: * Не полагайся на ADL без крайней нужды. * Используй ::qualified::names для ясности. * В шаблонах — лучше явно передавать функции (через policy, traits, или параметры шаблона). 📲 Мы в MAX ➡️ @cpp_geek1,13%
  • 6 июл.Пару фишек про шаблоны, которые могут спасти час дебага: 1. CTAD (Class Template Argument Deduction, C++17) Не надо вручную указывать аргументы: std::pair p(42, 3.14); // вместо std::pair<int, double> p(42, 3.14); std::vector v = {1,2,3}; // компилятор сам выведет std::vector<int> Помогает сократить код и избежать опечаток. 2. Fold-выражения (C++17) для арг-паков: auto sum = [](auto... args){ return (args + ...); // ((a + b) + c) + ... }; std::cout << sum(1,2,3,4); // 10 Позволяют писать операции над любым числом параметров без рекурсии. 3. SFINAE → Concepts (C++20) Старый стиль через enable_if легко сломать: template<class T> std::enable_if_t<std::is_integral_v<T>, T> foo(T x) { return x*2; } С Concepts чище и понятнее: template<std::integral T> T foo(T x) { return x*2; } 4. CRTP (Static polymorphism) Быстрее виртуалок и без RTTI: template<class D> struct Base { void interface() { static_cast<D*>(this)->impl(); } }; struct Derived : Base<Derived> { void impl() { std::cout<<"OK\n"; } }; Шаблоны — это не только про универсальность, но и про ясность кода. Освой тонкости, и они станут 🔧, а не головняком. ➡️ @cpp_geek1,09%
  • 14 мар.🎭 Сколько стоит virtual? Вся правда о полиморфизме и магии final Мы обожаем интерфейсы и ООП. Добавить virtual перед методом - минутное дело, и вот наш код уже гибкий и расширяемый. Но задумывались ли вы, чем мы за это платим на уровне железа? ⚙️ Анатомия виртуального вызова (vtable) Если в классе есть хотя бы одна виртуальная функция, компилятор втайне добавляет в каждый объект этого класса скрытое поле - vptr (указатель на виртуальную таблицу). Сама таблица (vtable) хранится где-то в памяти и содержит адреса реальных функций. Как происходит вызов obj->DoWork() под капотом: 1. Процессор идет по адресу объекта obj. 2. Читает скрытый указатель vptr. 3. Делает прыжок в память, где лежит vtable. 4. Находит там нужный адрес функции для конкретного класса-наследника. 5. Делает еще один прыжок, чтобы выполнить код. 🚨 Почему это бьет по производительности? Дело даже не в лишних прыжках по памяти (хотя промахи кэша процессора - это больно). Главная проблема: Виртуальность убивает оптимизации. Когда компилятор видит вызов виртуальной функции через указатель, он "слепнет". Он не знает, код какого именно наследника будет вызван во время работы программы (Run-time). Из-за этого он не может применить Inlining (встраивание тела функции вместо вызова) - а это самая мощная оптимизация в C++. 🛡 Спаситель из C++11: ключевое слово final Слово final запрещает дальнейшее наследование класса или переопределение метода. Но кроме защиты архитектуры, оно делает невероятное: включает Девиртуализацию (Devirtualization). class Base { public: virtual void Process() = 0; }; // Мы жестко фиксируем класс: от него нельзя наследоваться! class Derived final : public Base { public: void Process() override { /* важная логика */ } }; void RunOptimized(Derived* obj) { // Компилятор видит: тип obj — Derived. // Derived помечен как final. Значит, никто физически // не мог переопределить метод Process! // 🚀 МАГИЯ: Компилятор выбрасывает vtable, игнорирует vptr // и превращает вызов в обычный, или вообще инлайнит (встраивает) его! obj->Process(); } 💡 Золотое правило современного C++: Относитесь к классам как к запечатанным. Пишите final для всех классов (особенно тех, что реализуют интерфейсы), если только вы не проектируете их специально для дальнейшего наследования. Вы получите защиту от глупых архитектурных ошибок и бесплатный прирост скорости! #cpp #cpp11 #oop #optimization #performance #coding #tips ➡️ @cpp_geek1,07%
  • 14 янв.🏗 Что на самом деле происходит, когда std::vector «лопается»? Мы все любим push_back. Это удобно: кидаешь данные в вектор, а он сам разбирается с памятью. Но что происходит, когда вы добавляете элемент, а capacity (вместимость) вектора закончилась? Происходит Реаллокация (Reallocation). И это гораздо дороже, чем кажется. ⚙️ Алгоритм катастрофы: 1. Поиск новой земли: Вектор понимает, что места нет. Он обращается к оперативной памяти и просит выделить новый блок памяти. Обычно он в 1.5 или 2 раза больше текущего. 2. Великое переселение: Все элементы из старого блока памяти копируются (или перемещаются, если есть noexcept move-конструктор) в новый блок. ⚫️Если у вас там 1,000,000 тяжелых объектов - удачи процессору. 😅 3. Уничтожение: Для всех объектов в старом блоке вызываются деструкторы. 4. Снос: Старый блок памяти возвращается системе. 🚨 Почему это проблема? 1. Удар по производительности: Обычно push_back работает за O(1) (мгновенно). Но в момент реаллокации сложность подскакивает до O(N). Это создает непредсказуемые лаги (latency spikes). В системах реального времени (gamedev, high-load) это недопустимо. 2. Инвалидация итераторов и ссылок (ОПАСНО): Это источник багов №1. std::vector<int> vec = {1, 2, 3}; int& ref = vec[0]; // Ссылка на первый элемент // ... добавляем много элементов ... for(int i=0; i < 100; ++i) vec.push_back(i); // 💥 Вектор переехал в новую память. // Старая память удалена. ref теперь указывает в мусор. std::cout << ref; // Undefined Behavior (Crash) 🛡 Как лечить? Если вы хотя бы примерно знаете, сколько элементов будет в векторе, всегда используйте reserve(). std::vector<User> users; users.reserve(1000); // Сразу выделяем память // Теперь первые 1000 push_back будут дешевыми // и не вызовут реаллокации. 💡 Итог: std::vector это отличный инструмент, но за его «магию» расширения платит процессор. Помогайте ему через reserve(). #cpp #stdvector #performance #memory #coding #tips ➡️ @cpp_geek1,02%