Дана входная строка s, необходимо изменить порядок слов в строке на противоположный.
Слово определяется как последовательность символов, не содержащих пробелов. Слова в s будут разделены как минимум одним пробелом.
Вернуть строку, состоящую из слов в обратном порядке, объединенных одним пробелом.
*s может содержать начальные или конечные пробелы, а также несколько пробелов между двумя словами. Возвращаемая строка должна содержать только один пробел, разделяющий слова.
Способ решения:
- Определяем два указателя (left, right) на первый и последний символы без пробела.
- Начинаем обход слева направо. Когда получаем пробел, проверяем, был ли символ перед ним пробелом или нет. Если да, игнорируем его и продолжаем. Иначе добавим текущее слово (temp) перед текущим ответом (ans = temp + " " + ans)
- Как только дойдем до конца (left == right), добавляем последнее слово перед ответом аналогично с предыдущим пунктом.
- Стираем последний пробел в ответе (ans.erase(ans.begin() + ans.length() - 1);)
#разбор_кода
Слово определяется как последовательность символов, не содержащих пробелов. Слова в s будут разделены как минимум одним пробелом.
Вернуть строку, состоящую из слов в обратном порядке, объединенных одним пробелом.
*s может содержать начальные или конечные пробелы, а также несколько пробелов между двумя словами. Возвращаемая строка должна содержать только один пробел, разделяющий слова.
Способ решения:
- Определяем два указателя (left, right) на первый и последний символы без пробела.
- Начинаем обход слева направо. Когда получаем пробел, проверяем, был ли символ перед ним пробелом или нет. Если да, игнорируем его и продолжаем. Иначе добавим текущее слово (temp) перед текущим ответом (ans = temp + " " + ans)
- Как только дойдем до конца (left == right), добавляем последнее слово перед ответом аналогично с предыдущим пунктом.
- Стираем последний пробел в ответе (ans.erase(ans.begin() + ans.length() - 1);)
#разбор_кода
Libcurl
Libcurl — это библиотека для передачи файлов с множественным протоколом, которая поддерживает огромное множество протоколов. Также Libcurl поддерживает сертификаты SSL, HTTP POST, HTTP PUT, загрузку FTP, загрузку на основе форм HTTP и многое другое.
В этом примере создается объект CURL, затем вызывается curl_easy_init для его инициализации. Затем устанавливаются опции CURLOPT_URL и CURLOPT_WRITEFUNCTION с помощью curl_easy_setopt.
- CURLOPT_URL указывает URL-адрес для выполнения GET запроса.
- CURLOPT_WRITEFUNCTION указывает функцию обратного вызова для записи данных ответа.
Затем вызывается curl_easy_perform для выполнения запроса. После завершения запроса вызывается curl_easy_cleanup для очистки объекта CURL, а затем curl_global_cleanup для очистки глобальных ресурсов libcurl.
Результат выполнения этого кода — вывод содержимого страницы в стандартный вывод.
Libcurl — это библиотека для передачи файлов с множественным протоколом, которая поддерживает огромное множество протоколов. Также Libcurl поддерживает сертификаты SSL, HTTP POST, HTTP PUT, загрузку FTP, загрузку на основе форм HTTP и многое другое.
В этом примере создается объект CURL, затем вызывается curl_easy_init для его инициализации. Затем устанавливаются опции CURLOPT_URL и CURLOPT_WRITEFUNCTION с помощью curl_easy_setopt.
- CURLOPT_URL указывает URL-адрес для выполнения GET запроса.
- CURLOPT_WRITEFUNCTION указывает функцию обратного вызова для записи данных ответа.
Затем вызывается curl_easy_perform для выполнения запроса. После завершения запроса вызывается curl_easy_cleanup для очистки объекта CURL, а затем curl_global_cleanup для очистки глобальных ресурсов libcurl.
Результат выполнения этого кода — вывод содержимого страницы в стандартный вывод.
Обработка исключений
Обработка исключений осуществляется с помощью трех ключевых слов: try, catch и throw:
- try: блок кода, который может генерировать исключения, помещается в блок try. Если в блоке try происходит исключение, то выполнение блока прерывается и начинается поиск соответствующего блока catch.
- throw: throw используется для генерации исключения. Чаще всего используется внутри блока try. После ключевого слова throw указывается объект исключения, который будет передан в соответствующий блок catch.
- catch: блок catch следует за блоком try и используется для перехвата и обработки исключений. В скобках после ключевого слова catch указывается тип исключения, которое будет перехватываться этим блоком. Если тип исключения совпадает с типом сгенерированного исключения, то выполнение переходит в этот блок catch, где происходит обработка исключения.
В этом примере функция divide выдает исключение типа std::invalid_argument с сообщением об ошибке “Division by zero”.
#это_база
Обработка исключений осуществляется с помощью трех ключевых слов: try, catch и throw:
- try: блок кода, который может генерировать исключения, помещается в блок try. Если в блоке try происходит исключение, то выполнение блока прерывается и начинается поиск соответствующего блока catch.
- throw: throw используется для генерации исключения. Чаще всего используется внутри блока try. После ключевого слова throw указывается объект исключения, который будет передан в соответствующий блок catch.
- catch: блок catch следует за блоком try и используется для перехвата и обработки исключений. В скобках после ключевого слова catch указывается тип исключения, которое будет перехватываться этим блоком. Если тип исключения совпадает с типом сгенерированного исключения, то выполнение переходит в этот блок catch, где происходит обработка исключения.
В этом примере функция divide выдает исключение типа std::invalid_argument с сообщением об ошибке “Division by zero”.
#это_база
libjpg
Библиотека libjpg предназначена для работы с изображениями в формате JPEG в C++.
Основные возможности:
- Декодирование изображений JPEG из файла или памяти в растровое изображение;
- Кодирование растрового изображения в JPEG и сохранение в файл или память;
- Получение информации о JPEG изображении (размер, цветность и т. д.).
В этом примере мы декодируем тестовое изображение test.jpg, выводим некоторую информацию о нем (размер и цветность), а затем сохраняем результат декодирования в PNG файл test.png.
После запуска программы в папке появится файл test.png с растровым изображением, полученным из исходного JPEG файла test.jpg.
Библиотека libjpg предназначена для работы с изображениями в формате JPEG в C++.
Основные возможности:
- Декодирование изображений JPEG из файла или памяти в растровое изображение;
- Кодирование растрового изображения в JPEG и сохранение в файл или память;
- Получение информации о JPEG изображении (размер, цветность и т. д.).
В этом примере мы декодируем тестовое изображение test.jpg, выводим некоторую информацию о нем (размер и цветность), а затем сохраняем результат декодирования в PNG файл test.png.
После запуска программы в папке появится файл test.png с растровым изображением, полученным из исходного JPEG файла test.jpg.
Макросы в С++
Макросы — это директивы препроцессора, которые позволяют определять алиасы (псевдонимы) для констант, выражений или фрагментов кода.
Основные моменты, которые нужно знать о макросах:
- Определяются с помощью директив
- Макрос заменяется на значение уже на этапе препроцессинга, до компиляции;
- Могут принимать параметры через запись
- Раскрываются в коде путем подстановки, а не вызова функции;
- Имена макросов часто пишутся заглавными буквами.
Здесь мы определили макрос PI для числа pi и макрос MIN для нахождения минимума из двух значений.
При вызове MIN(x, y) подставляется выражение с параметрами, вычисляется минимум и результат записывается в min.
#это_база
Макросы — это директивы препроцессора, которые позволяют определять алиасы (псевдонимы) для констант, выражений или фрагментов кода.
Основные моменты, которые нужно знать о макросах:
- Определяются с помощью директив
#define;- Макрос заменяется на значение уже на этапе препроцессинга, до компиляции;
- Могут принимать параметры через запись
#define MACRO(a, b);- Раскрываются в коде путем подстановки, а не вызова функции;
- Имена макросов часто пишутся заглавными буквами.
Здесь мы определили макрос PI для числа pi и макрос MIN для нахождения минимума из двух значений.
При вызове MIN(x, y) подставляется выражение с параметрами, вычисляется минимум и результат записывается в min.
#это_база
#вопросы_с_собеседований
Перечислите все способы синхронизации процессов.
1. Использование мьютексов (std::mutex). Мьютекс позволяет захватывать его одним потоком, блокируя доступ другим потокам.
2. Использование семафоров (std::semaphore). Семафоры позволяют ограничивать количество потоков, которые могут одновременно захватить семафор.
3. Использование условных переменных (std::condition_variable). Они позволяют блокировать поток до наступления некоторого события.
4. Использование флагов и барьеров (std::atomic_flag, std::barrier).
5. Передача сообщений между потоками через очереди (std::queue).
6. Использование фьючерсов и промисов (std::promise, std::future).
Выбор конкретного механизма зависит от сценария использования и требований к синхронизации. Главное при этом избегать длительных и взаимных блокировок.
Перечислите все способы синхронизации процессов.
2. Использование семафоров (std::semaphore). Семафоры позволяют ограничивать количество потоков, которые могут одновременно захватить семафор.
3. Использование условных переменных (std::condition_variable). Они позволяют блокировать поток до наступления некоторого события.
4. Использование флагов и барьеров (std::atomic_flag, std::barrier).
5. Передача сообщений между потоками через очереди (std::queue).
6. Использование фьючерсов и промисов (std::promise, std::future).
Выбор конкретного механизма зависит от сценария использования и требований к синхронизации. Главное при этом избегать длительных и взаимных блокировок.
Дан целочисленный массив nums, вернуть [nums[i], nums[j], nums[k]] такие, что nums[i] + nums[j] + nums[k] == 0.
В решении ThreeSum работает следующим образом:
Сначала массив сортируется, затем в трёх вложенных циклах перебираются все возможные комбинации индексов i, j и k. Если их сумма равна 0, то тройка добавляется в результат.
#разбор_кода
В решении ThreeSum работает следующим образом:
Сначала массив сортируется, затем в трёх вложенных циклах перебираются все возможные комбинации индексов i, j и k. Если их сумма равна 0, то тройка добавляется в результат.
#разбор_кода
DynaPDF
DynaPDF — это библиотека для работы с PDF файлами. Она позволяет генерировать PDF документы, заполнять формы, добавлять изображения, текст и другое содержимое.
Основные возможности DynaPDF:
- Создание PDF документов с нуля или на основе шаблонов.
- Добавление страниц, изображений, текста, графики в документ.
- Работа со шрифтами, цветами, прозрачностью.
- Создание интерактивных элементов-ссылок, закладок, форм.
- Шифрование и защита паролем.
В этом примере создается объект pdf, добавляется страница, устанавливается шрифт и пишется текст "Hello DynaPDF!" в точке (100, 100).
После этого файл сохраняется как result.pdf.
DynaPDF — это библиотека для работы с PDF файлами. Она позволяет генерировать PDF документы, заполнять формы, добавлять изображения, текст и другое содержимое.
Основные возможности DynaPDF:
- Создание PDF документов с нуля или на основе шаблонов.
- Добавление страниц, изображений, текста, графики в документ.
- Работа со шрифтами, цветами, прозрачностью.
- Создание интерактивных элементов-ссылок, закладок, форм.
- Шифрование и защита паролем.
В этом примере создается объект pdf, добавляется страница, устанавливается шрифт и пишется текст "Hello DynaPDF!" в точке (100, 100).
После этого файл сохраняется как result.pdf.
const и volatile указатели
Const и volatile указатели используются для дополнительного контроля над данными, на которые они указывают.
- Const указатель гарантирует, что данные по этому указателю изменять нельзя.
- Volatile указатель сообщает компилятору, что значение данных может измениться в любой момент вне контроля программы.
#это_база
Const и volatile указатели используются для дополнительного контроля над данными, на которые они указывают.
- Const указатель гарантирует, что данные по этому указателю изменять нельзя.
- Volatile указатель сообщает компилятору, что значение данных может измениться в любой момент вне контроля программы.
#это_база
#вопросы_с_собеседований
Что такое aliasing?
Aliasing - это ситуация, когда две или более переменные ссылаются на одну и ту же область памяти. Это может привести к непредсказуемому поведению программы, так как изменение одной переменной повлияет на другую.
Примеры ситуаций, которые могут вызывать aliasing:
- Использование указателей или ссылок, указывающих на один и тот же объект.
- Передача одного и того же объекта в функцию по значению и по ссылке.
- Возврат ссылки или указателя из функции на локальный объект.
- Использование union для хранения разных типов данных.
Чтобы избежать проблем с aliasing, нужно следить за жизненным циклом объектов и не допускать ситуаций, когда два указателя указывают на одну область памяти после того, как один из объектов был уничтожен. Также полезно использовать const когда это возможно, чтобы явно указывать, что данные не должны изменяться.
Что такое aliasing?
Примеры ситуаций, которые могут вызывать aliasing:
- Использование указателей или ссылок, указывающих на один и тот же объект.
- Передача одного и того же объекта в функцию по значению и по ссылке.
- Возврат ссылки или указателя из функции на локальный объект.
- Использование union для хранения разных типов данных.
Чтобы избежать проблем с aliasing, нужно следить за жизненным циклом объектов и не допускать ситуаций, когда два указателя указывают на одну область памяти после того, как один из объектов был уничтожен. Также полезно использовать const когда это возможно, чтобы явно указывать, что данные не должны изменяться.
RCU (Read-Copy-Update)
RCU (Read-Copy-Update) - это механизм синхронизации доступа к данным в многопоточных приложениях.
Он позволяет избежать блокировок при чтении данных, делая копию для чтения и обновляя оригинал после того, как все чтения завершены.
RCU особенно полезен, когда чтение данных происходит гораздо чаще, чем запись.
В примере мы сначала берем rcu_read_lock, чтобы защитить чтение foo. В другом потоке происходит модификация foo с помощью rcu_assign_pointer, которая делает копию данных, а оригинал удаляется после synchronize_rcu.
Таким образом чтение в первом потоке происходит без блокировок и избегает конфликтов с изменением данных в другом потоке. RCU гарантирует, что указатель foo будет виден как старое значение в первом потоке.
RCU (Read-Copy-Update) - это механизм синхронизации доступа к данным в многопоточных приложениях.
Он позволяет избежать блокировок при чтении данных, делая копию для чтения и обновляя оригинал после того, как все чтения завершены.
RCU особенно полезен, когда чтение данных происходит гораздо чаще, чем запись.
В примере мы сначала берем rcu_read_lock, чтобы защитить чтение foo. В другом потоке происходит модификация foo с помощью rcu_assign_pointer, которая делает копию данных, а оригинал удаляется после synchronize_rcu.
Таким образом чтение в первом потоке происходит без блокировок и избегает конфликтов с изменением данных в другом потоке. RCU гарантирует, что указатель foo будет виден как старое значение в первом потоке.
Libpq
Libpq - это библиотека, которая предоставляет API для взаимодействия с СУБД PostgreSQL из приложений на С++.
Она используется, когда нужно подключиться к базе данных PostgreSQL и выполнять SQL-запросы из кода на C++.
Этот код подключается к БД PostgreSQL, выполняет запрос к таблице employees и выводит количество строк и столбцов в результате.
Libpq - это библиотека, которая предоставляет API для взаимодействия с СУБД PostgreSQL из приложений на С++.
Она используется, когда нужно подключиться к базе данных PostgreSQL и выполнять SQL-запросы из кода на C++.
Этот код подключается к БД PostgreSQL, выполняет запрос к таблице employees и выводит количество строк и столбцов в результате.
#вопросы_с_собеседований
Что такое функторы?
Функторы - это объекты классов, которые могут вызываться как функции.
Другими словами, функторы позволяют обращаться к объекту так, как если бы это была обычная функция.
Функторы полезны в следующих ситуациях:
- Позволяют инкапсулировать функциональность в классе, связывать ее с состоянием объекта.
- Дают возможность передавать объекты-функторы в функции и хранить их в структурах данных.
- Упрощают написание обобщенных алгоритмов, которым нужно выполнять некоторую функциональность, не завися от конкретных типов объектов.
- Используются в стандартной библиотеке C++, например при работе с потоками, для запуска функций в потоке.
Что такое функторы?
Другими словами, функторы позволяют обращаться к объекту так, как если бы это была обычная функция.
Функторы полезны в следующих ситуациях:
- Позволяют инкапсулировать функциональность в классе, связывать ее с состоянием объекта.
- Дают возможность передавать объекты-функторы в функции и хранить их в структурах данных.
- Упрощают написание обобщенных алгоритмов, которым нужно выполнять некоторую функциональность, не завися от конкретных типов объектов.
- Используются в стандартной библиотеке C++, например при работе с потоками, для запуска функций в потоке.
for-each циклы
Цикл for-each - это удобный способ перебрать все элементы контейнера (массива, вектора и т. д.). Синтаксис:
for (type element : container) {
// тело цикла
}
Цикл последовательно проходит по всем элементам контейнера и записывает каждый в переменную element.
#это_база
Цикл for-each - это удобный способ перебрать все элементы контейнера (массива, вектора и т. д.). Синтаксис:
for (type element : container) {
// тело цикла
}
Цикл последовательно проходит по всем элементам контейнера и записывает каждый в переменную element.
#это_база
set::erase()
Функция set::erase() используется для удаления элементов из контейнера set.
set::erase() принимает значение, которое нужно удалить, в качестве аргумента и удаляет из set все элементы, эквивалентные переданному значению.
Если такого значения нет в set, не делает ничего.
В этом примере создается set целых чисел, добавляются значения от 1 до 5. Затем вызывается numbers.erase(3) - это удалит элемент со значением 3 из set.
#это_база
Функция set::erase() используется для удаления элементов из контейнера set.
set::erase() принимает значение, которое нужно удалить, в качестве аргумента и удаляет из set все элементы, эквивалентные переданному значению.
Если такого значения нет в set, не делает ничего.
В этом примере создается set целых чисел, добавляются значения от 1 до 5. Затем вызывается numbers.erase(3) - это удалит элемент со значением 3 из set.
#это_база
#вопросы_с_собеседований
Как и для чего используется метапрограммирование шаблонов?
Метапрограммирование шаблонов используется для генерации кода программы на этапе компиляции.
Это позволяет создавать высокооптимизированные гибкие библиотеки, которые адаптируются под конкретные типы во время компиляции за счет метапрограммирования шаблонов.
Основные способы метапрограммирования шаблонов:
- Шаблонные метапрограммы.
- Концепции.
- Типовые списки.
- constexpr if.
- Шаблонные рекурсивные алгоритмы.
Как и для чего используется метапрограммирование шаблонов?
Это позволяет создавать высокооптимизированные гибкие библиотеки, которые адаптируются под конкретные типы во время компиляции за счет метапрограммирования шаблонов.
Основные способы метапрограммирования шаблонов:
- Шаблонные метапрограммы.
- Концепции.
- Типовые списки.
- constexpr if.
- Шаблонные рекурсивные алгоритмы.
#вопросы_с_собеседований
Каковы спецификаторы доступа в C++?
Спецификаторы доступа позволяют инкапсулировать данные класса и ограничивать интерфейс взаимодействия с объектами.
Это важный инструмент ООП для сокрытия внутренней реализации и предоставления только необходимого уровня доступа извне.
В C++ определены следующие спецификаторы доступа:
public - объявленные таким образом данные и методы класса доступны из любого места программы.
protected - защищенные данные и методы доступны только из самого класса и его подклассов.
private - закрытые элементы доступны только из тела самого класса, где они определены.
Кроме того, есть спецификатор default - когда явный спецификатор доступа не указан. Такие элементы доступны только из текущего класса и друзей.
Каковы спецификаторы доступа в C++?
Это важный инструмент ООП для сокрытия внутренней реализации и предоставления только необходимого уровня доступа извне.
В C++ определены следующие спецификаторы доступа:
public - объявленные таким образом данные и методы класса доступны из любого места программы.
protected - защищенные данные и методы доступны только из самого класса и его подклассов.
private - закрытые элементы доступны только из тела самого класса, где они определены.
Кроме того, есть спецификатор default - когда явный спецификатор доступа не указан. Такие элементы доступны только из текущего класса и друзей.
std::pair
std::pair - это шаблон класса из стандартной библиотеки, который инкапсулирует пару значений разных типов.
Он часто используется для возврата нескольких значений из функции.
В этом примере getStats возвращает пару значений - сумму и среднее элементов массива. С помощью std::pair эти значения упаковываются в один возвращаемый объект. В main используется structured binding для распаковки пары в переменные sum и avg.
std::pair - это шаблон класса из стандартной библиотеки, который инкапсулирует пару значений разных типов.
Он часто используется для возврата нескольких значений из функции.
В этом примере getStats возвращает пару значений - сумму и среднее элементов массива. С помощью std::pair эти значения упаковываются в один возвращаемый объект. В main используется structured binding для распаковки пары в переменные sum и avg.
nullptr
nullptr - это ключевое слово, которое используется для обозначения null-указателя (указателя, который не ссылается ни на какой объект).
Основные моменты, которые нужно знать о nullptr:
- nullptr предпочтительнее использовать вместо старого способа обозначения null-указателя - константы 0 или NULL.
- nullptr гарантированно преобразуется в любой тип указателя.
- Использование nullptr позволяет избежать ошибок по сравнению с константами вроде NULL и делает код более читабельным.
В примере мы проверяем указатель ptr на равенство nullptr.
#это_база
nullptr - это ключевое слово, которое используется для обозначения null-указателя (указателя, который не ссылается ни на какой объект).
Основные моменты, которые нужно знать о nullptr:
- nullptr предпочтительнее использовать вместо старого способа обозначения null-указателя - константы 0 или NULL.
- nullptr гарантированно преобразуется в любой тип указателя.
- Использование nullptr позволяет избежать ошибок по сравнению с константами вроде NULL и делает код более читабельным.
В примере мы проверяем указатель ptr на равенство nullptr.
#это_база
std::begin() и std::end()
Функции std::begin() и std::end() из заголовочного файла <iterator> используются для получения итераторов на начало и конец контейнера соответственно.
Это часто нужно при работе с алгоритмами STL.
В примере std::begin() вернёт итератор на первый элемент контейнера, а std::end() - на позицию за последним элементом.
Эти функции работают со всеми стандартными контейнерами (vector, list, array и т.д.) и позволяют универсально перебирать их элементы.
Функции std::begin() и std::end() из заголовочного файла <iterator> используются для получения итераторов на начало и конец контейнера соответственно.
Это часто нужно при работе с алгоритмами STL.
В примере std::begin() вернёт итератор на первый элемент контейнера, а std::end() - на позицию за последним элементом.
Эти функции работают со всеми стандартными контейнерами (vector, list, array и т.д.) и позволяют универсально перебирать их элементы.