Функция resize
Функция
Она динамически меняет количество элементов в контейнере на указанное число.
Например, для вектора
Если изначально элементов было меньше — новые будут инициализированы по умолчанию (нулями). Если было больше — лишние удалятся.
Также можно явно задать значение для инициализации:
#это_база
Функция
resize служит для изменения размера контейнеров, например вектора или deque. Она динамически меняет количество элементов в контейнере на указанное число.
Например, для вектора
numbers вызов:numbers.resize(100);Установит размер вектора в 100 элементов.
Если изначально элементов было меньше — новые будут инициализированы по умолчанию (нулями). Если было больше — лишние удалятся.
Также можно явно задать значение для инициализации:
numbers.resize(80, -1);Также
resize принимает вектор-шаблон для копирования значений при расширении.#это_база
Алгоритм stable_partition
Алгоритм
Он принимает начало и конец контейнера, а также условие в виде функции или лямбда-выражения.
В результате все элементы, для которых условие истинно, окажутся в начале контейнера, а остальные — в конце.
Отличие от
Это бывает важно, например, при разбиении по нескольким критериям.
В примере мы разделили вектор на две части — четные и нечетные числа. Благодаря
#это_база
Алгоритм
std::stable_partition используется для разбиения контейнера на две части по какому-либо условию. Он принимает начало и конец контейнера, а также условие в виде функции или лямбда-выражения.
В результате все элементы, для которых условие истинно, окажутся в начале контейнера, а остальные — в конце.
Отличие от
partition в том, что stable_partition сохраняет относительный порядок элементов. Те, что шли перед разбиением в одной группе, останутся в том же порядке после.Это бывает важно, например, при разбиении по нескольким критериям.
В примере мы разделили вектор на две части — четные и нечетные числа. Благодаря
stable_partition сохранен относительный порядок элементов в каждой части.#это_база
Ромбовидное наследование
Ромбовидное наследование (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.Как избежать deadlock
Deadlock (взаимная блокировка) возникает, когда два или более потока заблокированы в ожидании ресурса, который удерживается другим потоком.
Чтобы избежать deadlock, нужно следовать следующим правилам:
— Не блокировать ресурсы в разном порядке в разных потоках.
— Не удерживать блокировку во время выполнения долгих операций.
— Использовать
— Избегать вложенных блокировок одного и того же мьютекса.
— Применять порядок блокировки ресурсов, например, всегда в алфавитном порядке.
— Использовать мьютексы только для защиты данных, а не для управления логикой.
Соблюдая эти правила, можно предотвратить ситуации взаимной блокировки потоков и построить корректную многопоточную логику.
Deadlock (взаимная блокировка) возникает, когда два или более потока заблокированы в ожидании ресурса, который удерживается другим потоком.
Чтобы избежать deadlock, нужно следовать следующим правилам:
— Не блокировать ресурсы в разном порядке в разных потоках.
— Не удерживать блокировку во время выполнения долгих операций.
— Использовать
lock_guard или unique_lock вместо явных lock/unlock.— Избегать вложенных блокировок одного и того же мьютекса.
— Применять порядок блокировки ресурсов, например, всегда в алфавитном порядке.
— Использовать мьютексы только для защиты данных, а не для управления логикой.
Соблюдая эти правила, можно предотвратить ситуации взаимной блокировки потоков и построить корректную многопоточную логику.
#вопросы_с_собеседований
Что такое variadic templates?
Variadic templates — это функция шаблонов, которая позволяет определить функцию или класс с переменным количеством аргументов.
Эта возможность появилась в C++11.
Variadic templates позволяют создавать функции, которые могут принимать произвольное количество аргументов, не зная заранее их типов.
Это достигается за счет использования упаковки аргументов (pack expansion) и рекурсивных шаблонов.
Проще говоря, variadic templates расширяют возможности шаблонов и позволяют создавать гибкие и универсальные компоненты.
Что такое variadic templates?
Эта возможность появилась в C++11.
Variadic templates позволяют создавать функции, которые могут принимать произвольное количество аргументов, не зная заранее их типов.
Это достигается за счет использования упаковки аргументов (pack expansion) и рекурсивных шаблонов.
Проще говоря, variadic templates расширяют возможности шаблонов и позволяют создавать гибкие и универсальные компоненты.
#вопросы_с_собеседований
Что такое SIMD-инструкции?
SIMD-инструкции — это специальные команды процессора, которые работают с векторными регистрами и могут выполнять одну операцию над несколькими элементами данных параллельно.
Например, при сложении двух векторов из 4 float чисел, вместо 4 инструкций сложения, с SIMD можно выполнить одну команду, которая сложит эти вектора за одну операцию.
Основные преимущества SIMD:
— Повышение производительности за счет параллельных вычислений.
— Эффективное использование пропускной способности процессора.
— Уменьшение количества инструкций за счет векторизации.
— Оптимизация алгоритмов обработки массивов, матриц, фильтрации, графики.
Что такое SIMD-инструкции?
Например, при сложении двух векторов из 4 float чисел, вместо 4 инструкций сложения, с SIMD можно выполнить одну команду, которая сложит эти вектора за одну операцию.
Основные преимущества SIMD:
— Повышение производительности за счет параллельных вычислений.
— Эффективное использование пропускной способности процессора.
— Уменьшение количества инструкций за счет векторизации.
— Оптимизация алгоритмов обработки массивов, матриц, фильтрации, графики.
Алгоритм partial_sort
partial_sort — это алгоритм сортировки из стандартной библиотеки, который упорядочивает только часть контейнера.
Сортирует элементы в диапазоне [first, middle). Элементы справа от middle остаются без изменений
При работе с частью данных эффективнее полной сортировки. Внутри использует алгоритм quicksort.
Применение:
— Быстрый поиск K наибольших/наименьших элементов.
— Сортировка только части большого массива данных.
— Оптимизация производительности по сравнению с полной сортировкой.
#это_база
partial_sort — это алгоритм сортировки из стандартной библиотеки, который упорядочивает только часть контейнера.
Сортирует элементы в диапазоне [first, middle). Элементы справа от middle остаются без изменений
При работе с частью данных эффективнее полной сортировки. Внутри использует алгоритм quicksort.
Применение:
— Быстрый поиск K наибольших/наименьших элементов.
— Сортировка только части большого массива данных.
— Оптимизация производительности по сравнению с полной сортировкой.
#это_база
Алгоритм equal_range
equal_range — это алгоритм поиска из стандартной библиотеки, который находит диапазон элементов, эквивалентных заданному значению.
Принимает отсортированный диапазон, искомое значение и возвращает пару итераторов, задающих найденный диапазон.
Диапазон содержит все элементы, эквивалентные значению. Если элементов нет — итераторы будут указывать на один элемент.
Применение:
— Поиск всех элементов, соответствующих значению.
— Получение диапазона для последующей обработки.
— Высокоэффективная альтернатива линейному поиску.
#это_база
equal_range — это алгоритм поиска из стандартной библиотеки, который находит диапазон элементов, эквивалентных заданному значению.
Принимает отсортированный диапазон, искомое значение и возвращает пару итераторов, задающих найденный диапазон.
Диапазон содержит все элементы, эквивалентные значению. Если элементов нет — итераторы будут указывать на один элемент.
Применение:
— Поиск всех элементов, соответствующих значению.
— Получение диапазона для последующей обработки.
— Высокоэффективная альтернатива линейному поиску.
#это_база
#вопросы_с_собеседований
Расскажите об использовании realloc в контейнерах.
realloc используется в контейнерах динамической памяти, таких как vector, для изменения выделенной памяти при добавлении или удалении элементов.
realloc вызывается при заполнении текущего буфера в контейнере и память перевыделяется большими блоками (обычно в 2 раза больше). Это позволяет избежать постоянного выделения памяти заново.
Само перевыделение происходит автоматически, скрыто от разработчика.
При частых вызовах может привести к фрагментации памяти.
Расскажите об использовании realloc в контейнерах.
realloc вызывается при заполнении текущего буфера в контейнере и память перевыделяется большими блоками (обычно в 2 раза больше). Это позволяет избежать постоянного выделения памяти заново.
Само перевыделение происходит автоматически, скрыто от разработчика.
При частых вызовах может привести к фрагментации памяти.
#вопросы_с_собеседований
Расскажите о работе с сырыми указателями.
Работа с сырыми указателями (raw pointers) требует внимания к управлению памятью:
— Сырой указатель содержит только адрес памяти, без информации о длительности владения.
— Память под указатель выделяется вручную с помощью new и освобождается вручную с delete.
— Опасность утечек памяти при потере последнего указателя на объект.
— Нужно следить за правильностью вызовов new/delete во избежание ошибок.
— Может привести к проблемам при копировании указателей (неявное копирование объекта).
— Предпочтительно использовать умные указатели вроде unique_ptr для безопасности.
— Сырые указатели полезны для низкоуровневых оптимизаций производительности.
— Требуют явного кодирования работы с памятью в стиле Си.
Расскажите о работе с сырыми указателями.
— Сырой указатель содержит только адрес памяти, без информации о длительности владения.
— Память под указатель выделяется вручную с помощью new и освобождается вручную с delete.
— Опасность утечек памяти при потере последнего указателя на объект.
— Нужно следить за правильностью вызовов new/delete во избежание ошибок.
— Может привести к проблемам при копировании указателей (неявное копирование объекта).
— Предпочтительно использовать умные указатели вроде unique_ptr для безопасности.
— Сырые указатели полезны для низкоуровневых оптимизаций производительности.
— Требуют явного кодирования работы с памятью в стиле Си.
static_assert
static_assert — это механизм проверки условий компиляции. Он позволяет выдавать ошибку компиляции, если не выполняется некое условие.
Основные случаи использования:
— Проверка размера типов данных.
— Проверка наличия функций или методов у классов.
— Верификация определенных свойств на этапе компиляции.
— Проверка корректности шаблонных параметров.
— Выявление ошибок в зависимостях между типами данных.
Преимущества:
— Выявляет ошибки на этапе компиляции, не дожидаясь выполнения.
— Позволяет проверить условия, которые нельзя проверить во время выполнения.
— Улучшает читаемость кода за счет явных проверок.
static_assert широко используется в шаблонах и метапрограммировании.
static_assert — это механизм проверки условий компиляции. Он позволяет выдавать ошибку компиляции, если не выполняется некое условие.
Основные случаи использования:
— Проверка размера типов данных.
— Проверка наличия функций или методов у классов.
— Верификация определенных свойств на этапе компиляции.
— Проверка корректности шаблонных параметров.
— Выявление ошибок в зависимостях между типами данных.
Преимущества:
— Выявляет ошибки на этапе компиляции, не дожидаясь выполнения.
— Позволяет проверить условия, которые нельзя проверить во время выполнения.
— Улучшает читаемость кода за счет явных проверок.
static_assert широко используется в шаблонах и метапрограммировании.
Декомпозиция при объявлении (structural bindings)
Structural bindings — это возможность С++17 разложить объект на отдельные переменные прямо в месте объявления.
Позволяет избежать временных объектов при разборе структур, сокращает и упрощает код при работе со структурами.
Structural bindings активно используется в модульном тестировании для проверки структур и классов.
Также применяется для деструктуризации данных в функциональном программировании.
Structural bindings — это возможность С++17 разложить объект на отдельные переменные прямо в месте объявления.
Позволяет избежать временных объектов при разборе структур, сокращает и упрощает код при работе со структурами.
Structural bindings активно используется в модульном тестировании для проверки структур и классов.
Также применяется для деструктуризации данных в функциональном программировании.
std::array
В отличие от обычных C-style массивов,
Основные характеристики:
— Размер массива задается шаблонным параметром и не может изменяться во время выполнения.
— Элементы хранятся в последовательной памяти, что дает хорошую локальность и производительность.
— Поддерживает итераторы, можно использовать в циклах range-for.
— Имеет полезные методы —
— Автоматически инициализирует элементы по умолчанию.
— Передается по значению, в отличие от сырых указателей.
std::array — это шаблонный контейнерный тип данных, представляющий собой статический массив с фиксированным размером. В отличие от обычных C-style массивов,
std::array является полноценным объектом со всеми преимуществами ООП.Основные характеристики:
— Размер массива задается шаблонным параметром и не может изменяться во время выполнения.
— Элементы хранятся в последовательной памяти, что дает хорошую локальность и производительность.
— Поддерживает итераторы, можно использовать в циклах range-for.
— Имеет полезные методы —
size(), front(), back(), data() и др.— Автоматически инициализирует элементы по умолчанию.
— Передается по значению, в отличие от сырых указателей.
buf указатель
buf — это указатель на буфер (массив байтов), часто использующийся для работы с бинарными данными.
Объявляется как
В основном используется совместно с функциями
Часто применяется в сетевом программировании, криптографии.
buf — это указатель на буфер (массив байтов), часто использующийся для работы с бинарными данными.
Объявляется как
u_char *buf или unsigned char *buf. Хранит данные типа unsigned char. Используется для указания на выделенный буфер памяти, куда будут помещаться данные.В основном используется совместно с функциями
memcpy, memset и др. для копирования данных.Часто применяется в сетевом программировании, криптографии.
Токенизация строки
Токенизация строки — это процесс разбиения строки на токены (лексемы) — отдельные элементы, например слова, числа, операторы.
Для токенизации нужно:
— Разбить строку на токены при помощи разделителей, например пробелов.
— Классифицировать каждый токен — определить его тип (число, строка, оператор и т. д.)
— Преобразовать токены к нужному типу, например из строки в число.
— Сохранить результаты в подходящей структуре данных.
— Обрабатывать ошибки, например неверный формат числа.
Для разбиения строки на токены в С++ удобно использовать
Для хранения результатов часто используют структуры или классы, хранящие тип и значение токена.
Токенизация нужна для разбора входных данных, конфигурационных файлов, математических выражений и т. д.
#это_база
Токенизация строки — это процесс разбиения строки на токены (лексемы) — отдельные элементы, например слова, числа, операторы.
Для токенизации нужно:
— Разбить строку на токены при помощи разделителей, например пробелов.
— Классифицировать каждый токен — определить его тип (число, строка, оператор и т. д.)
— Преобразовать токены к нужному типу, например из строки в число.
— Сохранить результаты в подходящей структуре данных.
— Обрабатывать ошибки, например неверный формат числа.
Для разбиения строки на токены в С++ удобно использовать
stringstream.Для хранения результатов часто используют структуры или классы, хранящие тип и значение токена.
Токенизация нужна для разбора входных данных, конфигурационных файлов, математических выражений и т. д.
#это_база
rvalue
Константные ссылки или ссылки на
Неконстантные ссылки (
*
#это_база
Rvalue — это временный объект, который может быть перемещен или скопирован. Например, результат выражения или возвращаемое значение функции — это rvalue. Rvalues являются временными объектами, которые разрушаются после использования. Перемещение ресурсов из rvalue более эффективно, чем копирование.Константные ссылки или ссылки на
const (const T&) могут связываться только с lvalues.Неконстантные ссылки (
T&) могут связываться как с lvalues, так и с rvalues.*
Lvalue — объект с именем, например переменная.#это_база
#вопросы_с_собеседований
Как работают константные методы?
Константные методы — это методы, которые помечены модификатором final. Это означает, что тело метода не может быть переопределено в подклассах.
Константные методы часто используются, когда нужно предоставить клиентам неизменяемую реализацию некоторой функциональности. Например, утилитные классы часто содержат константные методы.
Основные характеристики константных методов:
— Могут вызываться на экземплярах класса, так как не являются статическими.
— Может обращаться к полям класса, даже нестатическим, т. к. вызывается на объекте класса.
— Может вызывать другие методы класса, в том числе не константные.
— Сигнатура константного метода в подклассе должна полностью совпадать с сигнатурой в суперклассе, иначе это будет перегрузка, а не переопределение.
Как работают константные методы?
Константные методы часто используются, когда нужно предоставить клиентам неизменяемую реализацию некоторой функциональности. Например, утилитные классы часто содержат константные методы.
Основные характеристики константных методов:
— Могут вызываться на экземплярах класса, так как не являются статическими.
— Может обращаться к полям класса, даже нестатическим, т. к. вызывается на объекте класса.
— Может вызывать другие методы класса, в том числе не константные.
— Сигнатура константного метода в подклассе должна полностью совпадать с сигнатурой в суперклассе, иначе это будет перегрузка, а не переопределение.
#вопросы_с_собеседований
Что такое глубокое копирование?
Глубокое копирование (deep copy) — это создание полной копии объекта, включая все его внутренние объекты и поля.
В Java глубокое копирование нужно реализовывать вручную, так как оператор присваивания и конструктор копирования создают поверхностную копию (shallow copy).
При поверхностном копировании копируются только поля текущего объекта. Внутренние объекты не копируются, а их ссылки просто переносятся в новый объект.
При глубоком копировании рекурсивно копируются также все вложенные объекты. Это позволяет разорвать связь между исходным объектом и копией.
Для глубокого копирования в Java используют:
— Переопределение метода clone().
— Сериализацию объекта.
— Вручную рекурсивно копировать все поля и вложенные объекты.
Глубокое копирование нужно, чтобы изменения в копии объекта не влияли на оригинал. Это важно для правильной работы программы.
Что такое глубокое копирование?
В Java глубокое копирование нужно реализовывать вручную, так как оператор присваивания и конструктор копирования создают поверхностную копию (shallow copy).
При поверхностном копировании копируются только поля текущего объекта. Внутренние объекты не копируются, а их ссылки просто переносятся в новый объект.
При глубоком копировании рекурсивно копируются также все вложенные объекты. Это позволяет разорвать связь между исходным объектом и копией.
Для глубокого копирования в Java используют:
— Переопределение метода clone().
— Сериализацию объекта.
— Вручную рекурсивно копировать все поля и вложенные объекты.
Глубокое копирование нужно, чтобы изменения в копии объекта не влияли на оригинал. Это важно для правильной работы программы.
Паттерн Strategy
Паттерн Strategy — это паттерн проектирования, который позволяет определять семейства связанных алгоритмов и делать их взаимозаменяемыми.
Это дает возможность выбирать конкретный алгоритм во время выполнения программы.
Основная идея паттерна Strategy заключается в том, чтобы вынести алгоритмы в отдельные классы-стратегии и передавать нужную стратегию в клиентский код.
Это паттерн используется, когда:
— Нужно использовать разные варианты одного и того же алгоритма в разных ситуациях.
— Нужно легко добавлять новые стратегии, не меняя существующий клиентский код.
— Нужно избавиться от условных операторов, выбирающих алгоритм.
Паттерн Strategy — это паттерн проектирования, который позволяет определять семейства связанных алгоритмов и делать их взаимозаменяемыми.
Это дает возможность выбирать конкретный алгоритм во время выполнения программы.
Основная идея паттерна Strategy заключается в том, чтобы вынести алгоритмы в отдельные классы-стратегии и передавать нужную стратегию в клиентский код.
Это паттерн используется, когда:
— Нужно использовать разные варианты одного и того же алгоритма в разных ситуациях.
— Нужно легко добавлять новые стратегии, не меняя существующий клиентский код.
— Нужно избавиться от условных операторов, выбирающих алгоритм.
std::byte
Это тип с фиксированным размером в 1 байт, в отличие от
Поддерживает все операции сдвига и битовые операции
— Для представления байтовых данных без неявных преобразований типов.
— В низкоуровневом коде, работающем с памятью, регистрами и т. д.
— В криптографии и работе с сетевыми данными.
— Для явного обозначения, что переменная содержит просто байт данных.
Преимущества
— Независим от платформы, в отличие от
— Повышает читаемость кода, явно указывая на тип "байт".
— Исключает ошибки преобразования к
std::byte — это тип данных, представляющий собой байт, введенный в С++17.Это тип с фиксированным размером в 1 байт, в отличие от
char, размер которого зависит от платформы. Гарантированно не имеет знака (unsigned)Поддерживает все операции сдвига и битовые операции
std::byte используется в следующих случаях:— Для представления байтовых данных без неявных преобразований типов.
— В низкоуровневом коде, работающем с памятью, регистрами и т. д.
— В криптографии и работе с сетевыми данными.
— Для явного обозначения, что переменная содержит просто байт данных.
Преимущества
std::byte:— Независим от платформы, в отличие от
char и uint8_t.— Повышает читаемость кода, явно указывая на тип "байт".
— Исключает ошибки преобразования к
int/bool при вычислениях.Побитовое копирование
Побитовое копирование — копирование данных из одного объекта в другой побитно, без каких-либо преобразований.
Оно используется для копирования структур и классов. Когда мы присваиваем один объект другому того же типа, происходит побитовое копирование.
Побитовое копирование быстрее обычного копирования, так как не требует вызова конструкторов и деструкторов. Но при этом копируются все данные объекта, даже те, которые не нужны.
Чтобы предотвратить побитовое копирование для класса, можно объявить конструктор копирования и оператор присваивания
Также для предотвращения побитового копирования можно использовать ключевое слово
#это_база
Побитовое копирование — копирование данных из одного объекта в другой побитно, без каких-либо преобразований.
Оно используется для копирования структур и классов. Когда мы присваиваем один объект другому того же типа, происходит побитовое копирование.
Побитовое копирование быстрее обычного копирования, так как не требует вызова конструкторов и деструкторов. Но при этом копируются все данные объекта, даже те, которые не нужны.
Чтобы предотвратить побитовое копирование для класса, можно объявить конструктор копирования и оператор присваивания
private. Тогда компилятор выдаст ошибку при попытке копирования.Также для предотвращения побитового копирования можно использовать ключевое слово
delete для этих методов.#это_база