Senior C++ Developer – Telegram
Senior C++ Developer
12.3K subscribers
1.36K photos
3 videos
610 links
Изучаем C++.

По вопросам сотрудничества: @adv_and_pr

РКН: https://www.gosuslugi.ru/snet/676e9a1e4e740947beca35ba
Download Telegram
Удаление всех вхождений элемента в массив

В этом методе сдвиньте нецелевой элемент в левую сторону.

• Проверьте, является ли текущий элемент целевым элементом или нет.
• Если это целевой элемент, увеличьте переменную cnt.
• После этого элемента все нецелевые элементы сдвинутся влево с промежутком (n-cnt).

Временная сложность: O(n)
Сложность пространства: O(1)

На картинке приведена программа на C ++ для удаления всех вхождений элемента из массива с использованием оптимизированного подхода.
deque (double-ended queue)

deque — это контейнерный класс, в который можно эффективно добавлять и удалять элементы как с начала, так и с конца, а также получать произвольный доступ к элементам по индексу.
deque фактически является двусторонней очередью.

deque, как и vector, поддерживает произвольный доступ к элементам контейнера, но в отличие от вектора также поддерживает добавление в начало контейнера. Кроме того, во внутренней реализации deque при изменении размера не выделяет новый массив в памяти для вмещения нового набора элементов, а манипулирует указателями.

В этом примере мы используем функции push_front() и push_back() для добавления элементов в конец и начало очереди, а pop_front() и pop_back() для удаления первого и последнего элементов очереди numbers.
#вопросы_с_собеседований
Что такое race condition?

Race condition — это ситуация, когда результат выполнения программы зависят от того, в каком порядке выполняются отдельные потоки или процессы.

При наличии race condition несколько потоков или процессов могут обращаться к общему ресурсу или переменной и пытаться изменить ее значение одновременно. Это может привести к непредсказуемым результатам и ошибкам в программе.

Чтобы этого избежать, стоит использовать мьютексы, семафоры или атомарные операции. Они позволят скоординировать доступ к общим ресурсам и обеспечить правильную последовательность выполнения операций в многопоточной среде.
#вопросы_с_собеседований
Что такое exception safety guarantee?

Exception safety guarantee — это гарантия, которую предоставляет код при возникновении исключительной ситуации (exception)
. Это означает, что код должен обрабатывать исключения таким образом, чтобы предотвратить утечку ресурсов и поддерживать объекты в согласованном состоянии.

Существует три уровня exception safety guarantee:

- basic: Гарантирует, что нет утечек ресурсов, но в случае исключения никаких дополнительных гарантий не предоставляется, и объекты могут быть изменены или оставлены в неполноценном состоянии.

- strong: Гарантирует, что нет утечек ресурсов и объекты остаются в исходном состоянии, если исключение возникает. Если операция не может быть выполнена, объекты не изменяются.

- no-throw: Гарантирует, что операция не вызывает исключений. Это самый высокий уровень гарантии безопасности исключений.

Разработчики должны обеспечивать соответствующий уровень exception safety для своих классов и функций, чтобы гарантировать правильное и безопасное поведение при обработке исключений.
Принцип RAII

Resource Acquisition Is Initialization является важным концептом, который связывает временный срок жизни объекта с его ресурсами. Суть принципа заключается в том, что ресурсы, такие как память, файлы, сетевые соединения и т.д., должны быть приобретены при инициализации объекта и автоматически освобождены при его уничтожении.

Принцип RAII позволяет сделать управление ресурсами более безопасным и эффективным, освобождая программиста от ручного управления ресурсами и предотвращая утечки ресурсов.

В этом примере автоматическое закрытие файла в деструкторе обеспечивает гарантированное освобождение ресурса, даже если происходит исключение.
А в функции main файл закрывается автоматически, при выходе из блока try.
Функции вместимости в строках

1. capacity() - функция возвращает ёмкость памяти, выделенную для строки, которая может быть равной или больше, чем размер самой строки. Дополнительное пространство выделяется таким образом, чтобы при добавлении новых символов в строку операции могли выполняться эффективно.
2. resize() - функция изменяет размер строки, его можно увеличивать или уменьшать.
3. length() - функция возвращает длину строки.
4. shrink_to_fit() - функция уменьшает ёмкость памяти строки, делает ее равной минимально возможной. Эта операция полезна для экономии дополнительной памяти, когда мы уверены, что больше не нужно добавлять символы.
#вопросы_с_собеседований
Чем отличается using от typedef?

Синтаксис:
- Синтаксис для создания псевдонима типа с помощью typedef следующий: typedef int MyInt — создает псевдоним типа MyInt для типа int.
- Синтаксис с using выглядит следующим образом: using MyInt = int — создает псевдоним типа MyInt для типа int.

Возможности:
using является более мощным инструментом по сравнению с typedef. Он может быть использован не только для создания псевдонимов типов, но также и для создания псевдонимов шаблонов, псевдонимов функций и даже для создания псевдонимов для наборов значений (enum).

Поддержка шаблонов:
using более гибкий при использовании с шаблонами. Например, можно создавать псевдонимы типов для шаблонных классов следующим образом: using MyContainer = std::vector<int>. Такой подход недоступен с помощью typedef.

Область видимости:
using создает псевдонимы типов в текущей области видимости, что может быть полезным для устранения конфликтов имен. typedef создает псевдонимы типов на уровне глобальной области видимости, что может вызывать проблемы с именами в больших проектах.
Dependency Injection

Dependency Injection (DI) — это паттерн проектирования, который позволяет управлять зависимостями между объектами.
Он помогает разделить создание объектов от их использования и обеспечить более гибкую и тестируемую архитектуру программы.

В DI объекты получают свои зависимости не напрямую, а через внешний источник, который их предоставляет. Этот источник называется контейнером внедрения зависимостей. Контейнер отвечает за создание и управление зависимостями, а объекты получают их через конструкторы, методы или свойства.
паттерн Observer

Это паттерн проектирования, который позволяет объектам автоматически оповещать другие объекты об изменениях в своем состоянии.
В этом паттерне есть два основных компонента: наблюдаемый объект (subject) и наблюдатели (observers).

Когда состояние наблюдаемого объекта изменяется, все зарегистрированные наблюдатели уведомляются об этом изменении и автоматически обновляются.

В этом примере, когда вызывается метод setData, он обновляет данные и вызывает метод notifyObservers, он уведомляет всех зарегистрированных наблюдателей, вызывая update и передавая новые данные.
move constructor

Move-конструктор — это специальный конструктор, который позволяет эффективно перемещать ресурсы из одного объекта в другой, без необходимости копирования данных.
Он используется для реализации семантики перемещения (move semantics) и оптимизации работы с временными объектами.

Move-конструктор принимает rvalue ссылку (&&) на объект, который будет перемещен, и выполняет простое копирование указателей на данные, а не их фактическое копирование.

Использование move-конструктора позволяет избежать лишних копирований данных и повысить производительность при работе с большими или ресурсоемкими объектами.
#вопросы_с_собеседований
Что такое критическая секция?

Критическая секция - это участок кода, в котором происходит доступ к общему ресурсу
(например, переменной или структуре данных), и который должен быть выполнен атомарно, то есть без возможности прерывания другими потоками.

Для обеспечения безопасного доступа к критической секции в C++ используются механизмы синхронизации, такие как мьютексы (std::mutex) и блокировки (std::lock_guard, std::unique_lock). Перед выполнением критической секции поток должен захватить мьютекс, блокируя его для других потоков. После завершения работы в критической секции мьютекс освобождается, позволяя другим потокам получить доступ к ресурсу.

Использование критических секций и мьютексов позволяет избежать состояний гонки (race conditions) и обеспечить корректную и безопасную работу с общими данными в многопоточных приложениях.
Visitor

Visitor является поведенческим паттерном проектирования, который позволяет добавлять новые операции к классам объектов, не изменяя их структуру
. Он достигается путем выделения операций в отдельный класс-посетитель, который посещает объекты классов и выполняет необходимые операции.

Основная идея паттерна Visitor заключается в том, чтобы объекты классов принимали "посетителя" и передавали себя в качестве аргумента методам "посетителя", соответствующим своему классу. "Посетитель", в свою очередь, реализует различные методы для обработки разных типов объектов.
std::set

Представляет собой контейнер, который содержит уникальные элементы, расположенные в отсортированном порядке.
Он реализован в виде сбалансированного дерева (обычно красно-черного дерева), что обеспечивает высокую эффективность операций вставки, удаления и поиска элементов.

Некоторые особенности std::set:

- Уникальность элементов: Каждый элемент в std::set является уникальным, в контейнере не может быть несколько одинаковых элементов.

- Сортировка элементов: std::set автоматически сортирует элементы по их значениям при вставке, это позволяет эффективно выполнять операции поиска.

- Динамическое изменение: std::set позволяет добавлять и удалять элементы из контейнера в любое время.
std::map

Представляет собой ассоциативный контейнер, который содержит пары ключ-значение, где каждый ключ уникален.

Особенности std::map в сравнении с std::set:

- std::map является контейнером, который хранит пары ключ-значение, где каждый ключ уникален, в отличие от std::set, который хранит уникальные значения без дополнительных значений.
- В std::map операции вставки, удаления, поиска элементов, а также автоматической сортировки, осуществляются по ключу, а в std::set это происходит по самому значению.
- В std::map вы можете обновлять значения элементов, используя их ключи. В std::set невозможно обновить значение элемента, поскольку значениями являются сами элементы.
Pipes

В C++ пайпы (pipes) представляют собой механизм для односторонней связи между процессами.
Они позволяют передавать данные из одного процесса в другой, где один процесс выступает в роли писателя (write end), а другой процесс выступает в роли читателя (read end) пайпа.

Для работы с пайпами вы можете использовать системные вызовы, такие как pipe, fork и функции чтения/записи (read и write), доступные в POSIX-совместимых операционных системах.

Обратите внимание, что дескрипторы чтения и записи пайпа должны быть закрыты в соответствующих процессах с помощью close, чтобы гарантировать правильное завершение операций чтения и записи.
Sockets

Сокеты используются для обмена данными между процессами на разных узлах в сети.
Они предоставляют низкоуровневый интерфейс для передачи данных через сетевое соединение.

Для работы с сокетами в C++ вы можете использовать библиотеку сокетов, такую как BSD Sockets или Boost.Asio.

Этот код демонстрирует простой обмен данными между сервером и клиентом с использованием сокетов в C++.
Обратите внимание, что в реальной сетевой разработке вам также может понадобиться обработка ошибок, управление соединениями и другие детали. Однако данный пример дает представление о базовых принципах использования сокетов в C++.
#вопросы_с_собеседований
Можно ли использовать exceptions в конструкторе / деструкторе?

Да, в C++ можно использовать исключения (exceptions) в конструкторе и деструкторе класса. Однако, следует быть внимательным при использовании исключений в этих частях кода и учитывать некоторые особенности.

В конструкторе:

- Если конструктор выбрасывает исключение, объект не будет полностью сконструирован, и память, выделенная под него, не будет освобождена автоматически. Это может привести к утечке ресурсов или некорректному состоянию программы. В таких случаях следует использовать RAII (Resource Acquisition Is Initialization), чтобы гарантировать правильное освобождение ресурсов при исключениях.
- Конструкторы должны быть безопасными в отношении исключений. Если конструктор может выбросить исключение, стоит использовать try-catch в самом конструкторе или передать исключение дальше в коде.

В деструкторе:

- Если деструктор выбрасывает исключение, стек будет развернут, и все оставшиеся деструкторы будут вызваны. Однако, при выбрасывании исключения из деструктора, следует быть осторожным, так как это может привести к неопределенному поведению программы.
- Деструкторы могут выполнять необходимые операции по очистке ресурсов или уведомлению о состоянии, но исключения следует перехватывать и обрабатывать в другом месте.
std::weak_ptr

std::weak_ptr является частью стандартной библиотеки и представляет собой "слабый указатель" на объект, управляемый std::shared_ptr.

Он позволяет получать доступ к объекту, на который ссылается std::shared_ptr, но не влияет на его счётчик ссылок. Такой подход полезен в ситуациях, когда мы хотим избежать возможности утечек памяти из-за циклических ссылок между объектами.

В этом примере мы создаем два объекта типа Node, а затем устанавливаем циклическую ссылку между ними. Если бы мы использовали std::shared_ptr вместо std::weak_ptr для хранения ссылок, объекты node1 и node2 никогда не были бы удалены, поскольку они бы взаимно ссылались друг на друга и счётчики ссылок не достигали бы нуля.

*На втором изображении представлен результат работы кода.