C++ geek
Канал в MAX
Учим C/C++ на примерах
Загружаем канал…
Сайт использует cookie для работы и аналитики. Мы используем файлы cookie для работы сайта и аналитики посещаемости. Подробнее в ПодробнееПолитике обработки данных и Правилах.
Канал в MAX
Учим C/C++ на примерах
Зачем 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") — друго…
Читать целиком🧵 Сегодня покажу вам, как удобно логгировать значения в отладке с помощью макроса, который реально упрощает жизнь. Когда мы отлаживаем код, часто хочется быстро вывести значения переменных. Писать std::cout << "x: " << x << std::endl; каждый раз — боль. Давайте упростим: #include <iostream> #define LOG(var) std::cout << #var << " = " << (var) << std::endl; int main() { int x = 42; double pi = 3.1415; LOG(x); // x = 42 LOG(pi); // pi = 3.1415 } Фишка в том, что #var превращает имя переменной в строку. А (var) - значение. 🔥 Такой макрос отлично заходит при написании алгоритмов, отладки функций, проверки значений — и при этом делает код аккуратнее. Хочешь улучшить - можно сделать вывод в файл или добавить таймстемпы.
Что такое виртуальный деструктор и зачем он используется в C++? В C++ виртуальный деструктор используется для правильного освобождения памяти при удалении объекта через указатель на базовый класс. Если базовый класс имеет виртуальный деструктор, то при удалении объекта через указатель на базовый класс будет вызван деструктор не только базового класса, но и всех его производных классов. Это позволяет избежать утечек памяти и неопределенного поведения при работе с полиморфными объектами. Если виртуального деструктора не объявлено в базовом классе, то при удалении производного объекта через указатель на базовый класс будут вызваны только деструкторы базового класса, что может привести к утечкам памяти и неопределенному поведению.
std::move vs std::forward: когда и что использовать Сегодня покажу вам коротко, чем отличаются std::move и std::forward. - std::move(obj) — безусловно превращает объект в rvalue. После этого объект считается "пустым" для повторного использования (в рамках контракта move). Используем, когда мы точно хотим "забрать" ресурсы. - std::forward<T>(obj) — условно делает rvalue, если изначально пришёл rvalue. То есть это "perfect forwarding" для шаблонных функций. Пример: #include <utility> #include <string> #include <iostream> template <typename T> void wrapper(T&& arg) { process(std::forward<T>(arg)); // сохраняет rvalue/lvalue-семантику } void process(const std::string& s) { std::cout << "Lvalue: " << s << '\n'; } void process(std::string&& s) { std::cout << "Rvalue: " << s << '\n'; }…
Читать целиком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 — владение в единственном числе, идеально для большинства случа…
Читать целикомСейчас покажу вам простой, но мощный приём для ускорения компиляции - разделение объявления и реализации шаблонов с использованием explicit instantiation. Все мы знаем, что шаблоны в C++ реализуются в заголовочных файлах. Это значит, что каждый .cpp файл, который включает такой заголовок, заново инстанцирует шаблон. Результат - долгое время компиляции и раздутый бинарник. 🔧 Что можно сделать? Разделяем интерфейс и реализацию: // MyTemplate.hpp #pragma once template<typename T> class MyTemplate { public: void doSomething(); }; // MyTemplate.cpp #include "MyTemplate.hpp" #include <iostream> template<typename T> void MyTemplate<T>::doSomething() { std::cout << "Doing something\n"; } // Явная инстанциация template class MyTemplate<int>; Теперь в клиентском коде: #include "MyTe…
Читать целикомЧто случится, если exception выйдет за пределы потока? Если exception выходит за пределы потока, то оно не может быть обработано на текущем уровне, т. к. он уже завершен. В таком случае исключение будет зарегистрировано как неперехваченное и может привести к аварийному завершению программы. Чтобы избежать данной ситуации, необходимо обернуть код, где может возникнуть исключение, в try-catch блок на том же уровне, что и поток, с которым он связан. ➡️ @channel_cpp_geek
Dependency Injection Dependency Injection (DI) — это паттерн проектирования, который позволяет управлять зависимостями между объектами. Он помогает разделить создание объектов от их использования и обеспечить более гибкую и тестируемую архитектуру программы. В DI объекты получают свои зависимости не напрямую, а через внешний источник, который их предоставляет. Этот источник называется контейнером внедрения зависимостей. Контейнер отвечает за создание и управление зависимостями, а объекты получают их через конструкторы, методы или свойства. ➡️ @channel_cpp_geek
Execution policy для параллельных алгоритмов Execution policy в C++ — это новшество, введенное в стандарте языка C++17. Это механизм, который позволяет выбрать, как именно должны выполняться алгоритмы в стандартной библиотеке: последовательно или параллельно. Существуют три варианта execution policy: - seq: выполняет алгоритм последовательно. - par: выполняет алгоритм параллельно, используя все доступные ядра процессора. - par_unseq: выполняет алгоритм параллельно и может использовать неупорядоченное исполнение. Execution policy может быть использован в комбинации с многими алгоритмами в стандартной библиотеке, такими как std::for_each, std::transform, std::reduce и другими. Например, код выше выполняет алгоритм std::for_each параллельно. ➡️ @channel_cpp_geek
В чем разница между git fetch и git pull? Разница между этими командами заключается в том, что когда вы используете команду git fetch, Git извлекает последние изменения из удаленного репозитория в ваш локальный репозиторий, но оставляет эти изменения в отдельной ветке git origin. А команда git pull извлекает и интегрирует (скачивает и сливает) последние изменения из удаленного репозитория в вашу текущую ветку работы. ➡️ @channel_cpp_geek