#вопросы_с_собеседований
Что такое 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. Поэтому её использование для оптимизации поведения или производительности может быть непереносимым в различных компиляторах или версиях.
Что такое copy elision и когда становится возможным? Какие особенности для разных стандартов?
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. Поэтому её использование для оптимизации поведения или производительности может быть непереносимым в различных компиляторах или версиях.
#вопросы_с_собеседований
Что такое динамический анализатор кода? Какие знаете?
Динамический анализатор кода C++ - это инструмент или программное обеспечение, которое анализирует код C++ во время выполнения или исполнения. Он выполняет различные проверки и инспекции кода для обнаружения потенциальных проблем, ошибок, утечек памяти, узких мест в производительности или других проблем во время выполнения. В отличие от статического анализа кода, который анализирует код без его выполнения, динамический анализ дает представление о поведении кода во время его выполнения.
Примеры:
Valgrind: Это мощный инструмент динамического анализа, в него входит Memcheck, который обнаруживает утечки памяти, некорректные обращения к памяти и другие ошибки, связанные с памятью.
AddressSanitizer (ASan): Это детектор ошибок памяти, встроенный в компиляторы Clang и GCC. Он обнаруживает такие ошибки памяти, как переполнение буфера, использование после освобождения и т.д. ASan проверяет код во время компиляции, внедряя проверки во время выполнения.
Dr. Memory: Это инструмент отладки памяти для Windows и Linux. Он обнаруживает такие ошибки, как утечки памяти, незаконный доступ к памяти и неинициализированное чтение памяти.
GNU Electric Fence: Это инструмент отладки, который помогает обнаружить переполнения буфера и другие ошибки, связанные с памятью. Он использует технику под названием "защитные страницы" для защиты выделения памяти и обнаружения незаконных обращений.
Что такое динамический анализатор кода? Какие знаете?
Примеры:
Valgrind: Это мощный инструмент динамического анализа, в него входит Memcheck, который обнаруживает утечки памяти, некорректные обращения к памяти и другие ошибки, связанные с памятью.
AddressSanitizer (ASan): Это детектор ошибок памяти, встроенный в компиляторы Clang и GCC. Он обнаруживает такие ошибки памяти, как переполнение буфера, использование после освобождения и т.д. ASan проверяет код во время компиляции, внедряя проверки во время выполнения.
Dr. Memory: Это инструмент отладки памяти для Windows и Linux. Он обнаруживает такие ошибки, как утечки памяти, незаконный доступ к памяти и неинициализированное чтение памяти.
GNU Electric Fence: Это инструмент отладки, который помогает обнаружить переполнения буфера и другие ошибки, связанные с памятью. Он использует технику под названием "защитные страницы" для защиты выделения памяти и обнаружения незаконных обращений.
std::unordered_map
std::unordered_map - это контейнерный класс, который предоставляет структуру данных, известную как хэш-карта или хэш-таблица. Он доступен начиная с C++11.
Контейнер std::unordered_map хранит элементы в виде пар ключ-значение, где каждый ключ уникален, а производительность зависит от качества хэш-функции, используемой для сопоставления ключей с базовыми корзинами.
std::unordered_map в качестве ключей поддерживает встроенные типы, типы, определяемые пользователем, и даже структуры или классы.
std::unordered_map - это контейнерный класс, который предоставляет структуру данных, известную как хэш-карта или хэш-таблица. Он доступен начиная с C++11.
Контейнер std::unordered_map хранит элементы в виде пар ключ-значение, где каждый ключ уникален, а производительность зависит от качества хэш-функции, используемой для сопоставления ключей с базовыми корзинами.
std::unordered_map в качестве ключей поддерживает встроенные типы, типы, определяемые пользователем, и даже структуры или классы.
- В этом примере мы создаем myMap, которая сопоставляет целые числа со строками. - С помощью функции insert() вставляются 3 пары ключ-значение. - Затем мы получаем доступ и печатаем значение, связанное с ключом 2, используя оператор []. - С помощью функции erase() удаляем значение с ключом 3. - Далее мы выполняем итерации по карте с помощью цикла for, чтобы вывести все пары ключ-значение. - Наконец, мы используем функцию find(), чтобы проверить, существует ли ключ 2 в карте.#вопросы_с_собеседований
Как разработать систему плагинов на С++?
Разработка системы подключаемых модулей на C++ включает в себя создание структуры, позволяющей динамическую загрузку и обнаружение подключаемых модулей во время выполнения.
Ниже приведен обзор соответствующих шагов:
1. Определите интерфейс подключаемых модулей: Этот интерфейс должен определять набор функций или классов, которые должны реализовывать подключаемые модули.
2. API для плагинов: Создайте API, который облегчает загрузку и управление подключаемыми модулями.
3. Динамическая загрузка библиотек: Используйте механизм динамической загрузки библиотек операционной системы для загрузки подключаемых модулей во время выполнения.
4. Обнаружение плагинов: Реализуйте механизм для обнаружения и регистрации доступных подключаемых модулей во время выполнения.
5. Жизненный цикл плагина: Определите жизненный цикл плагинов, включая инициализацию, настройку и очистку.
6. Связь с плагинами: Разработайте механизм связи между приложением и подключаемыми модулями. Это может быть достигнуто с помощью вызовов функций, обратных вызовов, систем событий или передачи сообщений, в зависимости от требований вашей системы плагинов.
7. Обработка ошибок: Реализуйте механизмы обработки ошибок для разрешения таких ситуаций, как неудачная загрузка плагина, несовместимые версии плагинов или ошибки времени выполнения плагинов. Это обеспечивает надежность и стабильность системы плагинов.
Стоит отметить, что разработка системы плагинов может быть сложной задачей, и есть существующие фреймворки и библиотеки, которые могут помочь упростить этот процесс. Некоторые популярные варианты в C++ включают Boost.Extension, Poco Foundation и Qt's Plugin System. Эти фреймворки предоставляют абстракции и инструменты для создания систем плагинов и могут сэкономить время и усилия разработчиков.
Не забывайте учитывать аспекты безопасности при разработке системы подключаемых модулей, поскольку загрузка внешнего кода может привести к потенциальным уязвимостям.
Как разработать систему плагинов на С++?
Ниже приведен обзор соответствующих шагов:
1. Определите интерфейс подключаемых модулей: Этот интерфейс должен определять набор функций или классов, которые должны реализовывать подключаемые модули.
2. API для плагинов: Создайте API, который облегчает загрузку и управление подключаемыми модулями.
3. Динамическая загрузка библиотек: Используйте механизм динамической загрузки библиотек операционной системы для загрузки подключаемых модулей во время выполнения.
4. Обнаружение плагинов: Реализуйте механизм для обнаружения и регистрации доступных подключаемых модулей во время выполнения.
5. Жизненный цикл плагина: Определите жизненный цикл плагинов, включая инициализацию, настройку и очистку.
6. Связь с плагинами: Разработайте механизм связи между приложением и подключаемыми модулями. Это может быть достигнуто с помощью вызовов функций, обратных вызовов, систем событий или передачи сообщений, в зависимости от требований вашей системы плагинов.
7. Обработка ошибок: Реализуйте механизмы обработки ошибок для разрешения таких ситуаций, как неудачная загрузка плагина, несовместимые версии плагинов или ошибки времени выполнения плагинов. Это обеспечивает надежность и стабильность системы плагинов.
Стоит отметить, что разработка системы плагинов может быть сложной задачей, и есть существующие фреймворки и библиотеки, которые могут помочь упростить этот процесс. Некоторые популярные варианты в C++ включают Boost.Extension, Poco Foundation и Qt's Plugin System. Эти фреймворки предоставляют абстракции и инструменты для создания систем плагинов и могут сэкономить время и усилия разработчиков.
Не забывайте учитывать аспекты безопасности при разработке системы подключаемых модулей, поскольку загрузка внешнего кода может привести к потенциальным уязвимостям.
#вопросы_с_собеседований
Что такое барьеры памяти?
В C++ барьеры памяти являются механизмами синхронизации, используемыми для контроля порядка и видимости операций с памятью в многопоточных программах. Они гарантируют, что операции с памятью выполняются в определенном порядке и что эффекты одной операции видны другим потокам предсказуемым образом.
Существует несколько типов барьеров памяти, которые можно использовать в C++:
- Барьер приобретения: Гарантирует, что последующие операции с памятью не могут быть переупорядочены до барьера.
- Барьер освобождения: Гарантирует, что предшествующие операции с памятью не могут быть переупорядочены после барьера.
- Полный барьер памяти: Обеспечивает семантику как приобретения, так и освобождения.
- Барьер чтения-записи: Гарантирует, что предшествующие операции чтения не могут быть переупорядочены последующими операциями записи.
В C++ барьеры памяти обычно реализуются с помощью атомарных операций или примитивов синхронизации, предоставляемых языком, таких как std::atomic_thread_fence, std::atomic_signal_fence, или мьютексов и переменных состояния.
Что такое барьеры памяти?
Существует несколько типов барьеров памяти, которые можно использовать в C++:
- Барьер приобретения: Гарантирует, что последующие операции с памятью не могут быть переупорядочены до барьера.
- Барьер освобождения: Гарантирует, что предшествующие операции с памятью не могут быть переупорядочены после барьера.
- Полный барьер памяти: Обеспечивает семантику как приобретения, так и освобождения.
- Барьер чтения-записи: Гарантирует, что предшествующие операции чтения не могут быть переупорядочены последующими операциями записи.
В C++ барьеры памяти обычно реализуются с помощью атомарных операций или примитивов синхронизации, предоставляемых языком, таких как std::atomic_thread_fence, std::atomic_signal_fence, или мьютексов и переменных состояния.
#вопросы_с_собеседований
Что такое CI/CD и какие преимущества дает разработчику?
CI/CD означает непрерывную интеграцию и непрерывное развертывание. Это набор практик и инструментов, используемых в разработке программного обеспечения для автоматизации процесса создания, тестирования и развертывания приложений.
Непрерывная интеграция включает в себя интеграцию изменений кода от нескольких разработчиков в общий репозиторий часто, как правило, несколько раз в день. Каждая интеграция запускает автоматизированный процесс сборки и тестирования для раннего обнаружения и решения проблем интеграции.
Непрерывное развертывание (или Continuous Delivery) направлено на автоматизацию процесса развертывания программного обеспечения в производственных средах. Он включает в себя автоматизацию шагов, необходимых для упаковки, развертывания и настройки приложения.
Вот некоторые преимущества CI/CD для разработчиков C++:
1. Раннее обнаружение ошибок.
2. Более быстрый цикл обратной связи.
3. Качество и ремонтопригодность кода.
4. Сотрудничество и коммуникация между разработчиками.
5. Автоматизация сборки, тестирования и развертывания кода.
6. Непрерывное развертывание.
7. Масштабируемость.
Что такое CI/CD и какие преимущества дает разработчику?
Непрерывная интеграция включает в себя интеграцию изменений кода от нескольких разработчиков в общий репозиторий часто, как правило, несколько раз в день. Каждая интеграция запускает автоматизированный процесс сборки и тестирования для раннего обнаружения и решения проблем интеграции.
Непрерывное развертывание (или Continuous Delivery) направлено на автоматизацию процесса развертывания программного обеспечения в производственных средах. Он включает в себя автоматизацию шагов, необходимых для упаковки, развертывания и настройки приложения.
Вот некоторые преимущества CI/CD для разработчиков C++:
1. Раннее обнаружение ошибок.
2. Более быстрый цикл обратной связи.
3. Качество и ремонтопригодность кода.
4. Сотрудничество и коммуникация между разработчиками.
5. Автоматизация сборки, тестирования и развертывания кода.
6. Непрерывное развертывание.
7. Масштабируемость.
Движок C3D Vision управляет отображением 3D-текстур «на лету»
Texture3D представляет стандартный C++ класс и служит для обработки 3D-текстур.
Смотреть статью
Texture3D представляет стандартный C++ класс и служит для обработки 3D-текстур.
Смотреть статью
Хабр
Движок C3D Vision управляет отображением 3D-текстур «на лету»
Движок визуализации C3D Vision пополнился новым функционалом. Теперь у пользователей есть возможность создания объемных текстур и отображения их в сцене. В этой заметке мы расскажем об объектах API...
std::conditional_variable
std::condition_variable является частью стандартной библиотеки и позволяет потоку ожидать наступления определенного условия перед продолжением выполнения.
Основной метод
В этом примере создается поток
std::condition_variable является частью стандартной библиотеки и позволяет потоку ожидать наступления определенного условия перед продолжением выполнения.
Основной метод
std::condition_variable - это wait(), который блокирует вызывающий поток до тех пор, пока другой поток не оповестит его с помощью метода notify_one() или notify_all(). Когда вызывается wait(), текущий поток освобождает блокировку, которая должна быть захвачена вместе с вызовом wait(), и ожидает, пока другой поток не вызовет один из методов оповещения. После получения оповещения поток пробуждается и пытается повторно захватить блокировку, и если блокировка успешно захватывается, поток может продолжить свою работу.В этом примере создается поток
workerThread, который ожидает сигнала от основного потока. Основной поток засыпает на 2 секунды, а затем устанавливает флаг ready в true и оповещает ожидающий поток с помощью notify_one(). workerThread пробуждается и выводит сообщение в консоль.std::atomic_flag
Метод
Метод
В этом примере создаются два потока
std::atomic_flag является классом для реализации простой атомарной флаговой переменной. Он предоставляет механизм безопасной работы с флагом в многопоточной среде без необходимости использования блокировок.std::atomic_flag имеет два основных метода: test_and_set() и clear():Метод
test_and_set() устанавливает флаг в "истину" и возвращает предыдущее значение флага. Если флаг уже был установлен, вызов test_and_set() вернет true, в противном случае он вернет false.Метод
clear() сбрасывает флаг в "ложь".В этом примере создаются два потока
workerThread1 и workerThread2, которые пытаются получить доступ к защищенному блоку кода. Флаг flag инициализируется значением true с помощью ATOMIC_FLAG_INIT. В цикле worker() каждый поток пытается вызвать test_and_set() для захвата флага. Если флаг уже установлен, поток ждет и повторно пытается его захватить. Когда флаг наконец захватывается, поток выполняет некоторую работу и вызывает clear() для освобождения флага.Базовый курс лекций по С++ из бакалавриата МФТИ
https://youtu.be/Bym7UMqpVEY?list=PL3BR09unfgciJ1_K_E914nohpiOiHnpsK
https://youtu.be/Bym7UMqpVEY?list=PL3BR09unfgciJ1_K_E914nohpiOiHnpsK
YouTube
Базовый курс C++ (MIPT, ILab). Lecture 1. Scent of C++.
Лекции в бакалавриате МФТИ по C++ на русском языке.
Это первая лекция базового курса. На этой лекции мы познакомимся с двумя базовыми механизмами языка C++, разберем его отличия от C.
Лектор: Константин Владимиров
Дата лекции: 13 сентября 2021 года
Съёмка:…
Это первая лекция базового курса. На этой лекции мы познакомимся с двумя базовыми механизмами языка C++, разберем его отличия от C.
Лектор: Константин Владимиров
Дата лекции: 13 сентября 2021 года
Съёмка:…
thread-safe пул потоков
На первом изображении представлена реализация потокобезопасного пула потоков. В этой реализации класс
На втором изображении представлен пример использования. функция
Обратите внимание, что приведенный здесь код является базовой реализацией пула потоков и не включает в себя расширенные возможности, такие как обработка возвращаемых значений от задач или ограничение количества одновременных задач. Вы можете расширить эту реализацию в соответствии с вашими конкретными потребностями.
На первом изображении представлена реализация потокобезопасного пула потоков. В этой реализации класс
ThreadPool управляет коллекцией рабочих потоков. Количество рабочих потоков определяется при создании пула потоков. Функция enqueue используется для передачи заданий в пул потоков.На втором изображении представлен пример использования. функция
Task передается в пул потоков с помощью функции enqueue. Функция Task просто печатает сообщение и "спит" в течение 1 секунды. После отправки всех заданий главный поток ждет некоторое время, чтобы дать заданиям завершиться перед выходом.Обратите внимание, что приведенный здесь код является базовой реализацией пула потоков и не включает в себя расширенные возможности, такие как обработка возвращаемых значений от задач или ограничение количества одновременных задач. Вы можете расширить эту реализацию в соответствии с вашими конкретными потребностями.
std::async
В C++
Функция
В этом примере функция
Функция
В C++
std::async - это шаблон функции, предоставляемый стандартной библиотекой <future>. Он используется для асинхронного выполнения функции или вызываемого объекта и получения объекта future, представляющего результат вычислений.Функция
std::async создает новый поток или использует существующий поток из пула потоков реализации для выполнения поставленной задачи. Она возвращает объект std::future, который можно использовать для получения результата или статуса вычисления.В этом примере функция
AddNumbers выполняется асинхронно с использованием std::async. Она принимает два целых числа в качестве аргументов и возвращает их сумму. Функция std::async вызывается с помощью AddNumbers и аргументов 5 и 10.Функция
std::async возвращает объект std::future<int>, который представляет собой результат вычисления. Вызывая функцию get() на объекте future, мы блокируем выполнение до завершения вычислений и получаем результат.std::launch::async
При использовании
В этом примере мы создаем асинхронную операцию с помощью
Затем мы вызываем
std::launch::async - это один из флагов, которые можно использовать при запуске асинхронной операции с помощью std::async. Он указывает, что операция должна быть запущена немедленно в отдельном потоке.При использовании
std::launch::async с std::async создается новый поток выполнения для асинхронной операции. Это означает, что операция будет выполняться параллельно с основным потоком, который вызвал std::async. Фактическое время запуска операции зависит от реализации, но обычно она начинается сразу после вызова std::async.В этом примере мы создаем асинхронную операцию с помощью
std::async и флага std::launch::async. Операция calculateSum складывает два числа, но перед этим засыпает на 2 секунды для имитации длительной работы.Затем мы вызываем
futureSum.get(), чтобы получить результат асинхронной операции. Если результат еще не готов, основной поток будет блокирован до его завершения.#вопросы_с_собеседований
Как размещается в памяти класс с множественным наследованием и виртуальными функциями в с++?
- Каждый класс в иерархии наследования имеет свой собственный блок памяти, называемый подобъектом базового класса.
- Порядок размещения подобъектов определяется порядком, в котором они перечислены при определении класса-наследника.
- Для каждого класса-потомка, содержащего виртуальные функции, компилятор создает таблицу виртуальных функций, она содержит указатели на виртуальные функции класса.
- У каждого объекта класса, содержащего виртуальные функции, есть указатель на его таблицу виртуальных функций (VTable pointer).
- Размер класса равен сумме размеров его подобъектов базовых классов плюс любые дополнительные данные, определенные в самом классе.
- При вызове виртуальной функции компилятор использует указатель на VTable объекта, чтобы найти правильную функцию для вызова.
*Реализация размещения таких классов может отличаться в зависимости от компилятора и платформы, на которой выполняется код. Описанный выше процесс является общим принципом, но некоторые детали могут различаться.
Как размещается в памяти класс с множественным наследованием и виртуальными функциями в с++?
- Порядок размещения подобъектов определяется порядком, в котором они перечислены при определении класса-наследника.
- Для каждого класса-потомка, содержащего виртуальные функции, компилятор создает таблицу виртуальных функций, она содержит указатели на виртуальные функции класса.
- У каждого объекта класса, содержащего виртуальные функции, есть указатель на его таблицу виртуальных функций (VTable pointer).
- Размер класса равен сумме размеров его подобъектов базовых классов плюс любые дополнительные данные, определенные в самом классе.
- При вызове виртуальной функции компилятор использует указатель на VTable объекта, чтобы найти правильную функцию для вызова.
*Реализация размещения таких классов может отличаться в зависимости от компилятора и платформы, на которой выполняется код. Описанный выше процесс является общим принципом, но некоторые детали могут различаться.
std::hash
Это структура шаблонного класса, определенная в заголовочном файле
*Для пользовательского типа данных требуется явная специализация структуры
Это структура шаблонного класса, определенная в заголовочном файле
<functional>. Она предоставляет хеш-функцию для хэширования различных типов данных, включая встроенные и пользовательские.std::hash используется, например, в ассоциативных контейнерах, таких как std::unordered_map и std::unordered_set, для быстрого доступа к элементам по ключу.*Для пользовательского типа данных требуется явная специализация структуры
std::hash для корректной работы хэширования.#вопросы_с_собеседований
Как работает spinlock?
Spinlock - это примитив синхронизации, который используется для организации доступа к общему ресурсу между несколькими потоками в многопоточной среде. Он работает по принципу активного ожидания (busy-waiting), когда поток, не получив доступ к ресурсу, повторно проверяет условие блокировки без перехода в режим ожидания или сна.
Основная идея spinlock состоит в том, чтобы поток, желающий получить доступ к ресурсу, повторял быструю проверку состояния блокировки в цикле, называемом "spin". Если блокировка уже удерживается другим потоком, текущий поток продолжает повторять проверку до тех пор, пока блокировка не будет освобождена.
Однако, важно отметить, что использование spinlock не всегда является эффективным. Если потоки занимаются активным ожиданием без перехода в режим ожидания, это может привести к высокому использованию процессорного времени. Spinlock особенно полезен, когда блокировки очень короткие и вероятность конфликтов невысока.
Как работает spinlock?
Основная идея spinlock состоит в том, чтобы поток, желающий получить доступ к ресурсу, повторял быструю проверку состояния блокировки в цикле, называемом "spin". Если блокировка уже удерживается другим потоком, текущий поток продолжает повторять проверку до тех пор, пока блокировка не будет освобождена.
Однако, важно отметить, что использование spinlock не всегда является эффективным. Если потоки занимаются активным ожиданием без перехода в режим ожидания, это может привести к высокому использованию процессорного времени. Spinlock особенно полезен, когда блокировки очень короткие и вероятность конфликтов невысока.
std::launch::deferred
Когда операция запускается с этим флагом, она не выполняется немедленно в отдельном потоке. Вместо этого операция откладывается до момента, когда вызывается
В этом примере мы создаем асинхронную операцию с помощью
Однако, когда мы вызываем
std::launch::deferred - это флаг, который может быть использован при запуске асинхронной операции с помощью std::async. Он указывает, что операция должна быть отложена и выполнена только при вызове функции get() на возвращаемом объекте std::future.Когда операция запускается с этим флагом, она не выполняется немедленно в отдельном потоке. Вместо этого операция откладывается до момента, когда вызывается
get() на объекте std::future. В этот момент операция будет выполнена синхронно в вызывающем потоке.В этом примере мы создаем асинхронную операцию с помощью
std::async и флага std::launch::deferred. Операция calculateSum складывает два числа и не имеет задержки.Однако, когда мы вызываем
std::future::get(), операция calculateSum будет выполнена синхронно в вызывающем потоке, а не в отдельном потоке. Это означает, что выполнение программы будет остановлено до завершения операции calculateSum.recursive mutex
Recursive mutex представляет собой механизм синхронизации, который позволяет одному потоку захватить мьютекс несколько раз без возникновения блокировки.
При каждом захвате поток должен также освободить мьютекс соответствующее количество раз, чтобы другие потоки могли получить доступ к нему.
При использовании
В этом примере потоки
Recursive mutex представляет собой механизм синхронизации, который позволяет одному потоку захватить мьютекс несколько раз без возникновения блокировки.
При каждом захвате поток должен также освободить мьютекс соответствующее количество раз, чтобы другие потоки могли получить доступ к нему.
При использовании
recursive mutex необходимо обеспечивать баланс между захватом и освобождением мьютекса, чтобы избежать утечек ресурсов и избыточной рекурсии.В этом примере потоки
t1 и t2 запускают recursiveFunction с разной глубиной и результат выполнения программы будет выводить глубину рекурсии, пока она не достигнет 0.Удаление всех вхождений элемента в массив
В этом методе сдвиньте нецелевой элемент в левую сторону.
• Проверьте, является ли текущий элемент целевым элементом или нет.
• Если это целевой элемент, увеличьте переменную cnt.
• После этого элемента все нецелевые элементы сдвинутся влево с промежутком (n-cnt).
Временная сложность: O(n)
Сложность пространства: O(1)
На картинке приведена программа на C ++ для удаления всех вхождений элемента из массива с использованием оптимизированного подхода.
В этом методе сдвиньте нецелевой элемент в левую сторону.
• Проверьте, является ли текущий элемент целевым элементом или нет.
• Если это целевой элемент, увеличьте переменную cnt.
• После этого элемента все нецелевые элементы сдвинутся влево с промежутком (n-cnt).
Временная сложность: O(n)
Сложность пространства: O(1)
На картинке приведена программа на C ++ для удаления всех вхождений элемента из массива с использованием оптимизированного подхода.
deque (double-ended queue)
deque — это контейнерный класс, в который можно эффективно добавлять и удалять элементы как с начала, так и с конца, а также получать произвольный доступ к элементам по индексу.
В этом примере мы используем функции
deque — это контейнерный класс, в который можно эффективно добавлять и удалять элементы как с начала, так и с конца, а также получать произвольный доступ к элементам по индексу.
deque фактически является двусторонней очередью.deque, как и vector, поддерживает произвольный доступ к элементам контейнера, но в отличие от вектора также поддерживает добавление в начало контейнера. Кроме того, во внутренней реализации deque при изменении размера не выделяет новый массив в памяти для вмещения нового набора элементов, а манипулирует указателями.В этом примере мы используем функции
push_front() и push_back() для добавления элементов в конец и начало очереди, а pop_front() и pop_back() для удаления первого и последнего элементов очереди numbers.