Изучаем C++. По вопросам сотрудничества: @adv_and_pr
#вопросы_с_собеседований
Что такое динамический анализатор кода? Какие знаете?
Динамический анализатор кода C++ - это инструмент или программное обеспечение, которое анализирует код C++ во время выполнения или исполнения. Он выполняет различные проверки и инспекции кода для обнаружения потенциальных проблем, ошибок, утечек памяти, узких мест в производительности или других проблем во время выполнения. В отличие от статического анализа кода, который анализирует код без его выполнения, динамический анализ дает представление о поведении кода во время его выполнения.
Примеры:
Valgrind: Это мощный инструмент динамического анализа, в него входит Memcheck, который обнаруживает утечки памяти, некорректные обращения к памяти и другие ошибки, связанные с памятью.
AddressSanitizer (ASan): Это детектор ошибок памяти, встроенный в компиляторы Clang и GCC. Он обнаруживает такие ошибки памяти, как переполнение буфера, использование после освобождения и т.д. ASan проверяет код во время компиляции, внедряя проверки во время выполнения.
Dr. Memory: Это инструмент отладки памяти для Windows и Linux. Он обнаруживает такие ошибки, как утечки памяти, незаконный доступ к памяти и неинициализированное чтение памяти.
GNU Electric Fence: Это инструмент отладки, который помогает обнаружить переполнения буфера и другие ошибки, связанные с памятью. Он использует технику под названием "защитные страницы" для защиты выделения памяти и обнаружения незаконных обращений.
#вопросы_с_собеседований
Как работает RTTI?
RTTI расшифровывается как "Run-Time Type Information" и является функцией в C++, которая предоставляет информацию о типе объекта во время выполнения. Она позволяет вам динамически запрашивать и манипулировать информацией о типе объекта.
В C++ информация о типе объектов обычно представлена механизмом, называемым классом type_info. Класс type_info является частью стандартной библиотеки C++ и определяется в заголовке <typeinfo>.
Чтобы использовать RTTI в C++, необходимо включить функцию RTTI, указав флаг компилятора -frtti или включив его в настройках проекта.
RTTI особенно полезен, когда у вас есть указатель на базовый класс или ссылка на объект производного класса. RTTI обеспечивает безопасную передачу указателя базового класса в производный класс и выполнение определенных операций. Для этого используется оператор dynamic_cast, который выполняет динамическое приведение и возвращает указатель или ссылку целевого типа, если приведение корректно, или нулевой указатель, или выбрасывает исключение, если приведение не удалось.
std::variant
Это функция C++17, обеспечивающая типобезопасное объединение, позволяющее хранить и манипулировать значениями разных типов в одном объекте. Она является частью стандартной библиотеки C++ и определена в заголовке <variant>
.
Шаблонный класс std::variant
похож на упрощенную версию union
, но с дополнительной безопасностью типов и поддержкой различных операций.
Основными функциями std::variant
являются index()
, valueless_by_exception()
, operator=
, emplace
.
В этом примере мы создаем объект var
, который может содержать значения типов int
, float
или std::string
. Мы присваиваем var
различные значения и получаем их с помощью std::get
.
Однако если мы попытаемся получить значение, используя неправильный тип (например, std::get<int>(var)
, когда вариант содержит std::string
), это вызовет исключение std::bad_variant_access
.
You Ain't Gonna Need It (YAGNI)
You Ain't Gonna Need It (Вам это не понадобится) - это принцип разработки программного обеспечения, который поощряет простоту и избегает чрезмерной инженерии. принцип YAGNI побуждает разработчиков реализовывать только те функции, которые необходимы в данный момент, а не добавлять функциональность, которая может потребоваться в будущем, но в данный момент не нужна.
Вот ключевые аспекты применения принципа YAGNI:
- Минимализм: Пишите минимальный объем кода, необходимый для выполнения непосредственных требований.
- Избегайте спекулятивной разработки: Не реализуйте функции на основе спекулятивных будущих требований.
- Рефакторинг: Перерабатывайте код, чтобы удалить все ненужные или неиспользуемые функции. Это помогает сохранить код компактным и удобным для обслуживания.
- Разработка, управляемая тестами: Пишите тесты для проверки необходимых функций, а не обширного тестирования гипотетических функциональных возможностей.
Помните, что принцип заключается не в ограничении гибкости, а в принятии обоснованных решений, основанных на реальных требованиях.
❓Что такое машина состояний в Qt, и как ее использовать?
Узнайте 16 мая в 20:00 на открытом уроке «StateMachine в Qt: что это такое и как можно использовать».
📢📢 На этом занятии мы рассмотрим концепцию машины состояний, как она реализована в Qt, и как ее можно использовать в ваших проектах.
Вебинар будет полезен разработчикам С++, а также разработчикам и архитекторам ПО, которые хотят научиться разрабатывать прикладное ПО на Qt.
🟠Занятие приурочено к запуску онлайн-курса «Разработка прикладного ПО на Qt и ОС Аврора» в OTUS. Возможны разные способы оплаты курса.
👉 Для участия в вебинаре пройдите вступительный тест: https://otus.pw/Ic2U/ Нативная интеграция. Информация о продукте www.otus.ru
KISS (Keep it Simple, Stupid)
Этот принцип программирования подразумевает использование наиболее простых и понятных решений. В С++ этот принцип может быть применен в различных областях, включая проектирование алгоритмов, написание кода и дизайн классов.
При проектировании алгоритмов важно использовать простые и понятные методы, которые легко понимаются другими программистами.
При написании кода важно использовать простые и понятные функции, которые выполняют только одну операцию. Например, вместо написания одной сложной функции, которая выполняет множество операций, можно написать несколько простых функций, которые выполняют каждую операцию отдельно.
При дизайне классов важно использовать простые и понятные методы, которые выполняют только одну операцию. Например, вместо создания одного сложного класса, который выполняет множество операций, можно создать несколько простых классов, каждый из которых выполняет отдельную операцию.
*На изображении представлен класс, удовлетворяющий принципу KISS
#вопросы_с_собеседований
Напишите базовую реализацию std::shared_ptr.
std::shared_ptr - умный указатель, который позволяет разделять владение объектом между несколькими shared_ptr. Когда последний shared_ptr уничтожается, он автоматически удаляет объект.
Спецификатор thread_local
Спецификатор thread_local
позволяет создавать переменные, которые будут иметь своё значение для каждого потока, в который они будут загружены. То есть, каждый поток будет иметь свою собственную копию этой переменной.
Это может быть полезным в ситуациях, когда необходимо иметь глобальную переменную, доступную для каждого потока, но значение этой переменной должно быть уникальным для каждого потока.
В данном примере мы создали переменную x
с помощью спецификатора thread_local
. Затем мы создали функцию increment()
, которая инкрементирует значение переменной x
и выводит его на экран.
В функции main()
мы создали два потока и передали им функцию increment()
. Каждый поток будет иметь свою собственную копию переменной x
, поэтому при выполнении функции increment()
в каждом потоке будет изменяться только своя копия переменной x
. После выполнения обоих потоков мы ожидаем завершения их работы с помощью метода join()
.
#вопросы_с_собеседований
Как тестировать закрытые методы?
Проверка закрытых методов в C++ может быть осуществлена путем написания тестовых случаев, которые используют публичные методы, которые в свою очередь вызывают закрытые методы. Этот подход называется "тестирование черного ящика" и позволяет тестировать функциональность закрытых методов, не раскрывая их реализацию.
RPC (Remote Procedure Call)
Это технология межпроцессного взаимодействия, которая позволяет вызывать процедуры на удаленном компьютере, как если бы они были локальными.
Существует несколько библиотек для RPC в C++, включая:
- Apache Thrift
- gRPC
- ONC/RPC
- XML-RPC++
Каждая из этих библиотек имеет свои особенности и применяется в разных случаях. Например, Apache Thrift
обычно используется для создания кросс-языковых приложений, а gRPC
- для создания масштабируемых и быстрых приложений на основе протокола HTTP/2.
В этом примере кода клиент использует gRPC
для вызова удаленной процедуры SayHello
на сервере. Он создает объект GreeterClient
, который использует gRPC
для установления связи с удаленным сервером и вызова метода SayHello
. Затем клиент получает ответ от сервера и выводит его на экран.
Coroutine
Корутины - это новый механизм в языке С++, который позволяет приостанавливать выполнение функции и возобновлять его с того же места позже.
Это особенно полезно в асинхронном программировании, когда нужно выполнить длительную операцию без блокировки потока выполнения. Корутины были добавлены в язык в стандарте C++20.
Данный пример демонстрирует создание генератора чисел Фибоначчи с использованием корутин. В функции fib
происходит вычисление чисел Фибоначчи, при этом выполнение функции приостанавливается с помощью ключевого слова co_yield
, когда нужно вернуть результат. В функции main
происходит использование генератора для вывода первых 10 чисел Фибоначчи на экран.
#вопросы_с_собеседований
Как можно вызвать функцию C в программе на C++?
Чтобы вызвать функцию на языке С в программе на C++, необходимо использовать директиву extern "C". Это позволяет компилятору C++ понимать синтаксис функций на языке С, а программисту написать программу, исходники которой частично написаны на С++, а частично на С.
Расширение CUDA
CUDA extension позволяет использовать функции NVIDIA CUDA в C++ коде. Оно предоставляет библиотеку из заголовочных файлов и библиотеку объектных файлов для компиляции. Преимущества использования расширения CUDA:-
возможность использования мощностей графического процессора (GPU) для ускорения вычислений;-
удобный синтаксис и возможность использования функций CUDA в C++ коде;-
поддержка различных операционных систем (Windows, Linux, macOS).
В данном примере мы объявляем ядро (kernel) add
, которое складывает два числа и записывает результат в указатель c
. Затем мы выделяем память на устройстве (GPU) для переменной dev_c
, вызываем ядро add
, копируем результат в переменную c
на хосте (CPU) и освобождаем память, выделенную на устройстве.
Обфускация
Обфускация - это процесс преобразования исходного кода программы в нечитаемый для человека вид, но сохраняющий работоспособность программы.
Чаще всего применяется для защиты кода от несанкционированного использования, взломов и раскрытия алгоритмов работы программы. Это может быть особенно важным для программ, которые содержат ценную информацию или используются в критических системах.
Такой код использует множество макросов, которые заменяют имена переменных и функций на случайные символы. Также, для усложнения чтения кода, используется множество пустых строк и лишних пробелов.
* На изображении слева представлен обычный код, а справа - его версия после обфускации
👩💻Как разрабатывать веб-сервисы на C# быстрее?
Узнайте на бесплатном вебинаре «ASP NET Core - подготовка и запуск простого веб-сервиса»: регистрация
На уроке мы:
— Создадим базовый web-api для сервиса;
— Разработаем контроллер и настроим маршрутизацию точек доступа;
— Подключим источник данных;
— Познакомимся с инструментами ручного тестирования сервиса и моделями развертывания приложения.
Спикером выступит Антон Герасименко, тимлид и .NET Developer.
Вебинар точно будет полезен C#-разработчикам с опытом от 2 лет, которые хотят углубиться в создание web-приложений.
👉Для регистрации пройдите вступительный тест: пройти тест на сайте
#вопросы_с_собеседований
Что такое copy elision и когда становится возможным? Какие особенности для разных стандартов?
copy elision - это техника оптимизации компилятора в C++, которая позволяет устранить ненужные операции копирования или перемещения при возврате объектов из функций или инициализации объектов. Это позволяет компилятору оптимизировать создание и уничтожение временных объектов, в результате чего код становится более эффективным.
copy elision становится возможной в сценариях, определенных стандартом C++. Соответствующее положение стандарта называется правилом "as-if", которое позволяет компилятору оптимизировать программу до тех пор, пока она производит такое же наблюдаемое поведение, как и исходный код.
- C++98/03: copy elision не является обязательной, но разрешена в качестве оптимизации. RVO* и NRVO* - обычные оптимизации, выполняемые компиляторами. *(Return Value Optimization, Named Return Value Optimization)
- C++11: Правила copy elision были пересмотрены. RVO и NRVO стали обязательными в некоторых случаях. copy elision также может происходить при выбросе исключений.
- C++17: Правила исключения копирования были еще более смягчены. Именованные переменные могут быть созданы или присвоены без необходимости выполнения операции перемещения. Эта оптимизация называется "mandatory copy elision".
- C++20: Правила исключения копирования остались такими же, как и в C++17.
Важно отметить, что хотя копирование является широко поддерживаемой оптимизацией, оно все еще зависит от реализации компилятором. Компиляторы могут иметь различное поведение или ограничения в отношении copy elision. Поэтому её использование для оптимизации поведения или производительности может быть непереносимым в различных компиляторах или версиях.
std::any
Это функция C++17, которая предоставляет безопасный с точки зрения типов контейнер для единичных значений любого типа. Она позволяет хранить и манипулировать значениями разных типов в одном объекте, подобно std::variant
. Однако, в отличие от std::variant
, который требует явного указания допустимых типов, std::any
может хранить значения любого типа.
Класс std::any
является частью стандартной библиотеки C++ и определяется в заголовке <any>
. std::any
предоставляет функции, такие как type()
, has_value()
, reset()
, emplace()
и другие, которые позволяют манипулировать и запрашивать хранимое значение.
В этом примере мы создаем объект val
, который может хранить значения любого типа. Мы присваиваем val
различные значения и извлекаем их с помощью std::any_cast
и проверки типа с помощью typeid
.
Однако при попытке извлечь значение, используя неправильный тип (в данном случае std::any_cast<int>
), возникает исключение std::bad_any_cast
, которое можно обработать с помощью try-catch.
Curiously Recurring Template Pattern (CRTP)
CRTP предполагает использование шаблонов и наследования для достижения формы статического полиморфизма. Он позволяет производному классу наследоваться от базового, который параметризуется самим производным классом в качестве аргумента шаблона.
В данном примере класс Base
является шаблонным классом, который принимает производный класс (Derived1
или Derived2
) в качестве аргумента шаблона. Класс Base
предоставляет общую функциональность или интерфейс, который может быть настроен каждым производным классом.
Функция implementation()
в базовом классе определяется как невиртуальная. Внутри функции implementation()
используется static_cast<Derived*>(this)
для приведения указателя к типу производного класса. Это позволяет каждому производному классу предоставлять свою собственную реализацию функции implementation()
.
Когда функция implementation()
вызывается на экземпляре производного класса, она вызывает соответствующую реализацию в этом производном классе.
#вопросы_с_собеседований
Почему рекомендуется не использовать паттерн Singleton?
Singleton гарантирует, что класс имеет только один экземпляр и обеспечивает глобальную точку доступа к нему. Однако паттерн Singleton часто критикуют и не рекомендуют использовать по нескольким причинам:
1. Глобальное состояние: Singleton вносит глобальное состояние в приложение, что может привести к тесной связи между классами и затруднить рассуждения о поведении системы.
2. Скрытие зависимостей: Singleton скрывает зависимости, затрудняя понимание и рассуждения о взаимодействии между различными компонентами системы.
3. Сложности тестирования: Singleton может усложнить модульное тестирование. Поскольку он полагается на глобальное состояние, становится трудно изолировать и тестировать отдельные компоненты по отдельности. Это может привести к зависимостям и сделать тесты более хрупкими.
4. Безопасность потоков: Экземпляры Singleton могут создавать проблемы с безопасностью потоков. Если несколько потоков пытаются одновременно получить доступ или изменить Singleton, это может привести к неопределенному поведению. А дополнительное обеспечение безопасности потоков добавляет сложности и может повлиять на производительность.
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
параллельно.
Хотите перейти с C++ на Go? Приходите 🗓 15 мая в 20:00 мск на открытый урок, где мы разберем слайсы и массивы простым языком на примерах.
По сравнению с C++, Golang также обладает более простым синтаксисом, более безопасным управлением памятью и встроенной поддержкой параллельного программирования. Он также обеспечивает более быструю компиляцию и запуск программ, что ускоряет процесс разработки. Это позволяет создавать более безопасные и эффективные приложения с меньшим количеством ошибок.
На занятии вы узнаете:
– Как устроены массивы и слайсы внутри
– Какие ошибки чаще всего допускают программисты при работе с ними
– Как применять эти знания на практике
👨💻 Занятие проведет Владимир Балун, ведущий разработчик в Ozon и преподаватель OTUS.
👉 Регистрируйтесь для участия — https://otus.pw/xDxT/
Вебинар приурочен к старту онлайн-курса «Golang Developer. Professional» в OTUS. Курс доступен в рассрочку.
Реклама. Информация о рекламодателе на сайте www.otus.ru.
🚀 Спроектируй cайт знакомств Tinder в прямом эфире!
📌 System Design interview — особенная секция собеседований разработчиков, где проверяется знание плюсов и минусов различных подходов при проектировании архитектуры, умение уточнять требования, а также способность кандидата проектировать сложные распределенные системы.
🗓 13 мая в 18:00 по МСК пройдет бесплатный открытый урок по проектированию сайта знакомств Tinder в прямом эфире!
Чему вы научитесь:
- проводить анализ требований;
- оценивать нагрузку на систему;
- проектировать сайт знакомств Tinder;
- проходить собеседования.
➡️ Регистрация на открытый урок
Valgrind
Valgrind - это набор инструментов для отладки и профилирования программного обеспечения, написанного на языке C/C++. Valgrind обнаруживает ошибки в использовании памяти, утечки памяти, профилирует программы, позволяя оптимизировать их производительность.
Valgrind состоит из нескольких инструментов, каждый из которых решает свою задачу. Например, Memcheck
обнаруживает ошибки памяти, Callgrind
профилирует программы, Cachegrind
анализирует использование кэша процессора.
В данном примере мы выделяем память под 10 целочисленных значений и записываем значение в ячейку с индексом 10, что приводит к чтению/записи за пределами выделенной памяти. Затем мы освобождаем выделенную память с помощью функции free
. Однако, ошибка не обнаруживается при компиляции и выполнении программы, но Valgrind может обнаружить ее при запуске программы.
SIMD (Single Instruction Multiple Data)
Это технология, позволяющая одновременно выполнять одну и ту же операцию над несколькими наборами данных. В современных процессорах SIMD-инструкции реализованы для ускорения работы с векторными операциями. В C++ SIMD-инструкции доступны через библиотеку immintrin.h
В данном примере функция addVectors
принимает на вход указатели на три массива a
, b
и c
, каждый из которых содержит 8 элементов типа float
. С помощью функции _mm256_loadu_ps
происходит загрузка первого и второго векторов в регистры, затем с помощью функции _mm256_add_ps
происходит их покомпонентное сложение. Результат сохраняется в третий вектор с помощью функции _mm256_storeu_ps
ext_aggregate
В стандарте C++17 появилась новая возможность для инициализации агрегатных объектов - ext_aggregate. Данная функциональность позволяет создавать объекты с помощью инициализации, используя списки инициализации.
В данном примере мы создаем объект типа Person
с помощью инициализации переменных через ext_aggregate. Используя точки и имена переменных, мы можем явно указать, какое значение присваивается каждой переменной.
Ранее в C++ для инициализации агрегатных объектов использовались фигурные скобки, которые позволяли инициализировать объекты только в том порядке, в котором объявлены переменные в структуре.
ext_aggregate позволяет инициализировать переменные в любом порядке, что делает код более читабельным и гибким.
Как программист, ты знаешь, что для постоянного развития нужно оставаться в курсе последних тенденций и новейших технологий. Но как найти время для этого в мире, где всегда карманы забиты проектами?
Вот где XOR приходит на помощь! Это не просто канал, это сообщество людей, которые разделяют твои интересы и страсти в мире программирования. Здесь ты найдешь свежие новости из индустрии, советы и трюки, а также не менее важную тему, мемы.
Не упускай возможность общаться с другими программистами, узнавать о новых проектах и продвижениях в сфере программирования, а также получать полезные советы для своей карьеры. Абсолютно бесплатно и всегда в твоем распоряжении.
Присоединяйся @xor_journal и не упусти возможности для постоянного роста и развития!
#вопросы_с_собеседований
Сколько раз будет выполняться этот цикл? Поясните свой ответ.
Если бы вы сказали 300, вы были бы правы, если бы i было объявлено как int. Однако, поскольку i было объявлено как unsigned char, правильным ответом будет то, что этот код приведет к бесконечному циклу.
Вот почему:
Выражение 2 * half_limit будет преобразовано в int (по правилам преобразования C++) и будет иметь значение 300. Однако, поскольку i - это беззнаковый символ, он будет пересчитан в 8-битное значение, которое, достигнув 255, переполнится (то есть вернется к 0), и цикл, таким образом, будет продолжаться вечно.
❓ Разрабатываешь на C++? Освой кроссплатформенную разработку на QT и ОС Аврора!
🔥 Пройди тест и проверь свои знания.
Ответишь — пройдешь на продвинутый курс "Разработка прикладного ПО на Qt и ОС Аврора" от OTUS по специальной цене! .
За 5 месяцев вы освоите одну из лучших кроссплатформенных библиотек для разработки UI на С++ — Qt, а также научитесь работать с развивающейся ОС Аврора. Благодаря этим навыкам вы сможете участвовать в проектах, разрабатывающих мобильные приложения для госкомпаний и крупного бизнеса.
👉 ПРОЙТИ ТЕСТ: https://otus.pw/TKWu/
🔥БОНУС
16 мая , в 20:00 (мск) пройдет бесплатный открытый урок курса «StateMachine в Qt: что это такое и как можно использовать» - запишись сегодня, напомним в день вебинара.Нативная интеграция. Информация о продукте www.otus.ru
#вопросы_с_собеседований
В чем проблема с этим кодом?
Поведение не определено, потому что деструктор не является виртуальным.
Из спецификации:
( C++11 §5.3.5/3 ) - если статический тип удаляемого объекта отличается от его динамического типа, статический тип должен быть базовым классом динамического типа удаляемого объекта, а статический тип должен иметь виртуальный деструктор, иначе поведение не определено.
#вопросы_с_собеседований
Что выводит код выше?
Вывод будет выглядеть так:
from A
from A
from Base
Здесь важно отметить порядок уничтожения классов и то, как Base метод возвращается к своей собственной реализации после уничтожения.