#вопросы_с_собеседований
Почему вызов container.size() перед каждой итерацией цикла является плохой практикой?
Вызов container.size() перед каждой итерацией по контейнеру не является оптимальным, так как это приводит к линейной сложности O(n).
Вместо этого лучше сохранить размер контейнера в переменную до цикла, таким образом размер вычисляется только один раз, а доступ к элементам в цикле происходит за константное время O(1).
Это позволяет избежать лишних вычислений размера на каждой итерации и улучшает производительность.
Почему вызов container.size() перед каждой итерацией цикла является плохой практикой?
Вместо этого лучше сохранить размер контейнера в переменную до цикла, таким образом размер вычисляется только один раз, а доступ к элементам в цикле происходит за константное время O(1).
Это позволяет избежать лишних вычислений размера на каждой итерации и улучшает производительность.
Почленное копирование
Почленное копирование — это копирование объекта с полным копированием его внутреннего состояния.
Оно используется для корректного копирования там, где побитовое копирование оказывается неэффективным.
Почленное копирование выполняется с помощью конструктора копирования и оператора присваивания. В них для каждого поля выполняется отдельное копирование.
Для указателей и выделенной памяти почленное копирование подразумевает выделение новой памяти и копирование данных по элементам.
Почленное копирование медленнее побитового, так как требует дополнительных операций. Зато оно корректно копирует состояние объекта.
Но, зато оно помогает избежать типичных ошибок, связанных с побитовым копированием.
Чтобы включить почленное копирование в класс, нужно определить для него конструктор копирования и оператор присваивания с нужной логикой копирования.
Можно также использовать ключевое слово
#это_база
Почленное копирование — это копирование объекта с полным копированием его внутреннего состояния.
Оно используется для корректного копирования там, где побитовое копирование оказывается неэффективным.
Почленное копирование выполняется с помощью конструктора копирования и оператора присваивания. В них для каждого поля выполняется отдельное копирование.
Для указателей и выделенной памяти почленное копирование подразумевает выделение новой памяти и копирование данных по элементам.
Почленное копирование медленнее побитового, так как требует дополнительных операций. Зато оно корректно копирует состояние объекта.
Но, зато оно помогает избежать типичных ошибок, связанных с побитовым копированием.
Чтобы включить почленное копирование в класс, нужно определить для него конструктор копирования и оператор присваивания с нужной логикой копирования.
Можно также использовать ключевое слово
copy для полей класса, чтобы компилятор автоматически сгенерировал код копирования для них.#это_база
Побитовое копирование
Побитовое копирование — копирование данных из одного объекта в другой побитно, без каких-либо преобразований.
Оно используется для копирования структур и классов. Когда мы присваиваем один объект другому того же типа, происходит побитовое копирование.
Побитовое копирование быстрее обычного копирования, так как не требует вызова конструкторов и деструкторов. Но при этом копируются все данные объекта, даже те, которые не нужны.
Чтобы предотвратить побитовое копирование для класса, можно объявить конструктор копирования и оператор присваивания
Также для предотвращения побитового копирования можно использовать ключевое слово
#это_база
Побитовое копирование — копирование данных из одного объекта в другой побитно, без каких-либо преобразований.
Оно используется для копирования структур и классов. Когда мы присваиваем один объект другому того же типа, происходит побитовое копирование.
Побитовое копирование быстрее обычного копирования, так как не требует вызова конструкторов и деструкторов. Но при этом копируются все данные объекта, даже те, которые не нужны.
Чтобы предотвратить побитовое копирование для класса, можно объявить конструктор копирования и оператор присваивания
private. Тогда компилятор выдаст ошибку при попытке копирования.Также для предотвращения побитового копирования можно использовать ключевое слово
delete для этих методов.#это_база
std::byte
Это тип с фиксированным размером в 1 байт, в отличие от
Поддерживает все операции сдвига и битовые операции
— Для представления байтовых данных без неявных преобразований типов.
— В низкоуровневом коде, работающем с памятью, регистрами и т. д.
— В криптографии и работе с сетевыми данными.
— Для явного обозначения, что переменная содержит просто байт данных.
Преимущества
— Независим от платформы, в отличие от
— Повышает читаемость кода, явно указывая на тип "байт".
— Исключает ошибки преобразования к
std::byte — это тип данных, представляющий собой байт, введенный в С++17.Это тип с фиксированным размером в 1 байт, в отличие от
char, размер которого зависит от платформы. Гарантированно не имеет знака (unsigned)Поддерживает все операции сдвига и битовые операции
std::byte используется в следующих случаях:— Для представления байтовых данных без неявных преобразований типов.
— В низкоуровневом коде, работающем с памятью, регистрами и т. д.
— В криптографии и работе с сетевыми данными.
— Для явного обозначения, что переменная содержит просто байт данных.
Преимущества
std::byte:— Независим от платформы, в отличие от
char и uint8_t.— Повышает читаемость кода, явно указывая на тип "байт".
— Исключает ошибки преобразования к
int/bool при вычислениях.Паттерн Strategy
Паттерн Strategy — это паттерн проектирования, который позволяет определять семейства связанных алгоритмов и делать их взаимозаменяемыми.
Это дает возможность выбирать конкретный алгоритм во время выполнения программы.
Основная идея паттерна Strategy заключается в том, чтобы вынести алгоритмы в отдельные классы-стратегии и передавать нужную стратегию в клиентский код.
Это паттерн используется, когда:
— Нужно использовать разные варианты одного и того же алгоритма в разных ситуациях.
— Нужно легко добавлять новые стратегии, не меняя существующий клиентский код.
— Нужно избавиться от условных операторов, выбирающих алгоритм.
Паттерн Strategy — это паттерн проектирования, который позволяет определять семейства связанных алгоритмов и делать их взаимозаменяемыми.
Это дает возможность выбирать конкретный алгоритм во время выполнения программы.
Основная идея паттерна Strategy заключается в том, чтобы вынести алгоритмы в отдельные классы-стратегии и передавать нужную стратегию в клиентский код.
Это паттерн используется, когда:
— Нужно использовать разные варианты одного и того же алгоритма в разных ситуациях.
— Нужно легко добавлять новые стратегии, не меняя существующий клиентский код.
— Нужно избавиться от условных операторов, выбирающих алгоритм.
#вопросы_с_собеседований
Что такое глубокое копирование?
Глубокое копирование (deep copy) — это создание полной копии объекта, включая все его внутренние объекты и поля.
В Java глубокое копирование нужно реализовывать вручную, так как оператор присваивания и конструктор копирования создают поверхностную копию (shallow copy).
При поверхностном копировании копируются только поля текущего объекта. Внутренние объекты не копируются, а их ссылки просто переносятся в новый объект.
При глубоком копировании рекурсивно копируются также все вложенные объекты. Это позволяет разорвать связь между исходным объектом и копией.
Для глубокого копирования в Java используют:
— Переопределение метода clone().
— Сериализацию объекта.
— Вручную рекурсивно копировать все поля и вложенные объекты.
Глубокое копирование нужно, чтобы изменения в копии объекта не влияли на оригинал. Это важно для правильной работы программы.
Что такое глубокое копирование?
В Java глубокое копирование нужно реализовывать вручную, так как оператор присваивания и конструктор копирования создают поверхностную копию (shallow copy).
При поверхностном копировании копируются только поля текущего объекта. Внутренние объекты не копируются, а их ссылки просто переносятся в новый объект.
При глубоком копировании рекурсивно копируются также все вложенные объекты. Это позволяет разорвать связь между исходным объектом и копией.
Для глубокого копирования в Java используют:
— Переопределение метода clone().
— Сериализацию объекта.
— Вручную рекурсивно копировать все поля и вложенные объекты.
Глубокое копирование нужно, чтобы изменения в копии объекта не влияли на оригинал. Это важно для правильной работы программы.
Как избежать deadlock
Deadlock (взаимная блокировка) возникает, когда два или более потока заблокированы в ожидании ресурса, который удерживается другим потоком.
Чтобы избежать deadlock, нужно следовать следующим правилам:
— Не блокировать ресурсы в разном порядке в разных потоках.
— Не удерживать блокировку во время выполнения долгих операций.
— Использовать
— Избегать вложенных блокировок одного и того же мьютекса.
— Применять порядок блокировки ресурсов, например, всегда в алфавитном порядке.
— Использовать мьютексы только для защиты данных, а не для управления логикой.
Соблюдая эти правила, можно предотвратить ситуации взаимной блокировки потоков и построить корректную многопоточную логику.
Deadlock (взаимная блокировка) возникает, когда два или более потока заблокированы в ожидании ресурса, который удерживается другим потоком.
Чтобы избежать deadlock, нужно следовать следующим правилам:
— Не блокировать ресурсы в разном порядке в разных потоках.
— Не удерживать блокировку во время выполнения долгих операций.
— Использовать
lock_guard или unique_lock вместо явных lock/unlock.— Избегать вложенных блокировок одного и того же мьютекса.
— Применять порядок блокировки ресурсов, например, всегда в алфавитном порядке.
— Использовать мьютексы только для защиты данных, а не для управления логикой.
Соблюдая эти правила, можно предотвратить ситуации взаимной блокировки потоков и построить корректную многопоточную логику.
Ромбовидное наследование
Ромбовидное наследование (diamond inheritance) — это ситуация, когда класс наследуется от нескольких базовых классов, которые в свою очередь наследуются от общего предка.
Например:
На изображении класс
При вызове
Ромбовидное наследование (diamond inheritance) — это ситуация, когда класс наследуется от нескольких базовых классов, которые в свою очередь наследуются от общего предка.
Например:
class A { };
class B : public A { };
class C : public A { };
class D : public B, public C { };
Здесь класс D наследуется от B и C, которые оба наследуются от класса A. Получается ромбовидная иерархия наследования.На изображении класс
D наследуется от B и C, которые в свою очередь наследуют метод print() от A.При вызове
printAll() метод print() вызывается дважды — по пути наследования через B и через C.Когда производительность упирается в железо, а когда в архитектуру? Как проектировать надежные и быстрые системы на C++? Какие подходы используют разработчики компиляторов, рантаймов и системного ПО?
Ответы на эти и другие вопросы найдем на C++ Russia — конференции для C++ разработчиков, инженеров, разработчиков компиляторов, тимлидов и исследователей.
📅 7 мая 2026 — онлайн-день
📅 16–17 мая 2026 — Москва + онлайн
Три дня докладов, воркшопов и общения C++ сообщества. Будем говорить про язык и инженерные задачи: архитектуру, производительность, управление памятью, многопоточность и разработку низкоуровневого ПО.
Новое в этом году — системное программирование: компиляторы, рантаймы, операционные системы, управление ресурсами и дизайн языков программирования.
В карточках собрали несколько топовых докладов из программы.
Используйте промокод, чтобы купить персональный билет со скидкой —
Купить билет
Реклама. ООО «Джуг Ру Груп». ИНН 7801341446
Ответы на эти и другие вопросы найдем на C++ Russia — конференции для C++ разработчиков, инженеров, разработчиков компиляторов, тимлидов и исследователей.
📅 7 мая 2026 — онлайн-день
📅 16–17 мая 2026 — Москва + онлайн
Три дня докладов, воркшопов и общения C++ сообщества. Будем говорить про язык и инженерные задачи: архитектуру, производительность, управление памятью, многопоточность и разработку низкоуровневого ПО.
Новое в этом году — системное программирование: компиляторы, рантаймы, операционные системы, управление ресурсами и дизайн языков программирования.
В карточках собрали несколько топовых докладов из программы.
Используйте промокод, чтобы купить персональный билет со скидкой —
SENIORCPPКупить билет
Реклама. ООО «Джуг Ру Груп». ИНН 7801341446
Алгоритм stable_partition
Алгоритм
Он принимает начало и конец контейнера, а также условие в виде функции или лямбда-выражения.
В результате все элементы, для которых условие истинно, окажутся в начале контейнера, а остальные — в конце.
Отличие от
Это бывает важно, например, при разбиении по нескольким критериям.
В примере мы разделили вектор на две части — четные и нечетные числа. Благодаря
#это_база
Алгоритм
std::stable_partition используется для разбиения контейнера на две части по какому-либо условию. Он принимает начало и конец контейнера, а также условие в виде функции или лямбда-выражения.
В результате все элементы, для которых условие истинно, окажутся в начале контейнера, а остальные — в конце.
Отличие от
partition в том, что stable_partition сохраняет относительный порядок элементов. Те, что шли перед разбиением в одной группе, останутся в том же порядке после.Это бывает важно, например, при разбиении по нескольким критериям.
В примере мы разделили вектор на две части — четные и нечетные числа. Благодаря
stable_partition сохранен относительный порядок элементов в каждой части.#это_база
#вопросы_с_собеседований
Что такое SIMD-инструкции?
SIMD-инструкции — это специальные команды процессора, которые работают с векторными регистрами и могут выполнять одну операцию над несколькими элементами данных параллельно.
Например, при сложении двух векторов из 4 float чисел, вместо 4 инструкций сложения, с SIMD можно выполнить одну команду, которая сложит эти вектора за одну операцию.
Основные преимущества SIMD:
— Повышение производительности за счет параллельных вычислений.
— Эффективное использование пропускной способности процессора.
— Уменьшение количества инструкций за счет векторизации.
— Оптимизация алгоритмов обработки массивов, матриц, фильтрации, графики.
Что такое SIMD-инструкции?
Например, при сложении двух векторов из 4 float чисел, вместо 4 инструкций сложения, с SIMD можно выполнить одну команду, которая сложит эти вектора за одну операцию.
Основные преимущества SIMD:
— Повышение производительности за счет параллельных вычислений.
— Эффективное использование пропускной способности процессора.
— Уменьшение количества инструкций за счет векторизации.
— Оптимизация алгоритмов обработки массивов, матриц, фильтрации, графики.
#вопросы_с_собеседований
Что такое variadic templates?
Variadic templates — это функция шаблонов, которая позволяет определить функцию или класс с переменным количеством аргументов.
Эта возможность появилась в C++11.
Variadic templates позволяют создавать функции, которые могут принимать произвольное количество аргументов, не зная заранее их типов.
Это достигается за счет использования упаковки аргументов (pack expansion) и рекурсивных шаблонов.
Проще говоря, variadic templates расширяют возможности шаблонов и позволяют создавать гибкие и универсальные компоненты.
Что такое variadic templates?
Эта возможность появилась в C++11.
Variadic templates позволяют создавать функции, которые могут принимать произвольное количество аргументов, не зная заранее их типов.
Это достигается за счет использования упаковки аргументов (pack expansion) и рекурсивных шаблонов.
Проще говоря, variadic templates расширяют возможности шаблонов и позволяют создавать гибкие и универсальные компоненты.
#вопросы_с_собеседований
Какая разница между std::map и std::unordered_map?
std::map — это ассоциативный контейнер на основе красно-черного дерева. Элементы хранятся в отсортированном порядке по ключу. Сложность операций O(log N).
std::unordered_map реализован как хеш-таблица. Элементы хранятся в произвольном порядке. В среднем сложность операций O(1).
Основные различия между std::map и std::unordered_map:
— Поиск, вставка и удаление в std::map за O(log N) в худшем случае. В std::unordered_map за O(1) в среднем.
— Итераторы std::map позволяют перебирать элементы в отсортированном порядке. Порядок элементов std::unordered_map произвольный.
— Map поддерживает бинарный поиск lower_bound(), upper_bound(), а unordered_map — нет.
— В unordered_map нельзя использовать указатели в качестве ключей в хеш-таблице. В map можно.
Какая разница между std::map и std::unordered_map?
std::unordered_map реализован как хеш-таблица. Элементы хранятся в произвольном порядке. В среднем сложность операций O(1).
Основные различия между std::map и std::unordered_map:
— Поиск, вставка и удаление в std::map за O(log N) в худшем случае. В std::unordered_map за O(1) в среднем.
— Итераторы std::map позволяют перебирать элементы в отсортированном порядке. Порядок элементов std::unordered_map произвольный.
— Map поддерживает бинарный поиск lower_bound(), upper_bound(), а unordered_map — нет.
— В unordered_map нельзя использовать указатели в качестве ключей в хеш-таблице. В map можно.
#вопросы_с_собеседований
Чем отличается конструктор копирования от оператора присваивания?
Конструктор копирования:
— Вызывается при создании нового объекта на основе существующего.
— Имеет сигнатуру ClassName(const ClassName&).
— Обычно выполняет полное копирование данных из одного объекта в другой.
Оператор присваивания:
— Вызывается при присваивании значений между существующими объектами.
— Имеет сигнатуру ClassName& operator=(const ClassName&).
— Часто выполняет поверхностное копирование, присваивая ресурсы.
Различия:
— Конструктор копирования создает новый объект, оператор присваивания — нет.
— Конструктор вызывается автоматически, оператор — явно программистом.
— Конструктор вызывается один раз, оператор может вызываться многократно.
Чем отличается конструктор копирования от оператора присваивания?
— Вызывается при создании нового объекта на основе существующего.
— Имеет сигнатуру ClassName(const ClassName&).
— Обычно выполняет полное копирование данных из одного объекта в другой.
Оператор присваивания:
— Вызывается при присваивании значений между существующими объектами.
— Имеет сигнатуру ClassName& operator=(const ClassName&).
— Часто выполняет поверхностное копирование, присваивая ресурсы.
Различия:
— Конструктор копирования создает новый объект, оператор присваивания — нет.
— Конструктор вызывается автоматически, оператор — явно программистом.
— Конструктор вызывается один раз, оператор может вызываться многократно.
#вопросы_с_собеседований
Зачем делать explicit-конструктор?
Explicit-конструктор используется для преобразования типов с явным указанием желаемого типа. Это позволяет избежать неявных преобразований и потенциальных ошибок.
Основные причины использовать explicit-конструктора:
— Предотвратить неявные преобразования, которые могут привести к потере данных.
— Избежать вызова конструктора при копировании объекта.
— Принудительно вызывать конструктор только при явном преобразовании типов.
— Улучшить читаемость кода, делая преобразования типов очевидными.
Зачем делать explicit-конструктор?
Основные причины использовать explicit-конструктора:
— Предотвратить неявные преобразования, которые могут привести к потере данных.
— Избежать вызова конструктора при копировании объекта.
— Принудительно вызывать конструктор только при явном преобразовании типов.
— Улучшить читаемость кода, делая преобразования типов очевидными.
Алгоритм is_partitioned
Алгоритм is_partitioned — это алгоритм из стандартной библиотеки algorithm, который проверяет, разбит ли диапазон элементов определенным образом.
Принимает три параметра:
— Два итератора, задающих проверяемый диапазон.
— Предикат (функцию или функтор), определяющий разбиение.
Возвращает bool значение — true если диапазон разбит согласно предикату и false в противном случае.
Этот алгоритм предпочтительнее цикла, т. к. проще в использовании и читабельнее, ведь он эффективно проверяет условие за один проход по последовательности.
В примере мы определяем предикат isEven для проверки четности числа, передаем его в is_partitioned вместе с вектором v и выводим результат.
#это_база
Алгоритм is_partitioned — это алгоритм из стандартной библиотеки algorithm, который проверяет, разбит ли диапазон элементов определенным образом.
Принимает три параметра:
— Два итератора, задающих проверяемый диапазон.
— Предикат (функцию или функтор), определяющий разбиение.
Возвращает bool значение — true если диапазон разбит согласно предикату и false в противном случае.
Этот алгоритм предпочтительнее цикла, т. к. проще в использовании и читабельнее, ведь он эффективно проверяет условие за один проход по последовательности.
В примере мы определяем предикат isEven для проверки четности числа, передаем его в is_partitioned вместе с вектором v и выводим результат.
#это_база