Есть ли преимущества одного фрагмента кода перед другим? Если да, то какие?
Хотя оба варианта выполняют одно и то же, второй вариант лучше с точки зрения производительности. Это связано с тем, что оператор постинкремента itr++ дороже, чем оператор предварительного инкремента ++itr. Базовая реализация оператора постинкремента создает копию элемента перед его увеличением, а затем возвращает копию.
Тем не менее, многие компиляторы автоматически оптимизируют первый вариант, преобразовывая его во второй.
Тем не менее, многие компиляторы автоматически оптимизируют первый вариант, преобразовывая его во второй.
#вопросы_с_собеседований
Возможно ли иметь рекурсивную встроенную функцию?
Хотя вы можете вызывать встроенную функцию из самой себя, компилятор может не генерировать встроенный код, поскольку не может определить глубину рекурсии во время компиляции.
Компилятор с хорошим оптимизатором может встраивать рекурсивные вызовы до некоторой глубины, зафиксированной во время компиляции (скажем, три или пять рекурсивных вызовов), и вставлять нерекурсивные вызовы во время компиляции для случаев, когда фактическая глубина будет превышена во время выполнения.
Возможно ли иметь рекурсивную встроенную функцию?
Компилятор с хорошим оптимизатором может встраивать рекурсивные вызовы до некоторой глубины, зафиксированной во время компиляции (скажем, три или пять рекурсивных вызовов), и вставлять нерекурсивные вызовы во время компиляции для случаев, когда фактическая глубина будет превышена во время выполнения.
Метод exchange()
Этот метод позволяет заменить значение переменной на новое значение и вернуть старое значение.
В данном примере значение переменной
Cледует заметить, что данная функция является безопасной для многопоточной обработки, т.к. она гарантирует атомарность выполнения замены значения переменной.
Хотя этот метод не является очень распространенным, он часто используется для обмена значениями двух переменных, что может пригодиться для реализации алгоритмов, таких как quicksort, где нужно обменивать элементы массива.
Этот метод позволяет заменить значение переменной на новое значение и вернуть старое значение.
В данном примере значение переменной
a было заменено на 10, а старое значение (5) было сохранено в переменной b, которую вернул метод std::exchange().Cледует заметить, что данная функция является безопасной для многопоточной обработки, т.к. она гарантирует атомарность выполнения замены значения переменной.
Хотя этот метод не является очень распространенным, он часто используется для обмена значениями двух переменных, что может пригодиться для реализации алгоритмов, таких как quicksort, где нужно обменивать элементы массива.
ACID (Atomicity, Consistency, Isolation, Durability)
В языке C++ концепция ACID может использоваться при работе с базами данных, например, при использовании библиотеки SQLite. ACID гарантирует, что транзакции будут выполнены одновременно и когда они закончатся, база данных находится в согласованном состоянии.
Atomicity гарантирует, что транзакция будет выполнена как одно целое и либо полностью, либо не выполнится вообще.
Consistency гарантирует, что данные в базе будут согласованы с правилами и ограничениями базы данных.
Isolation гарантирует, что транзакции будут выполнены независимо друг от друга и не будут влиять друг на друга.
Durability гарантирует, что данные в базе данных сохранятся даже в случае сбоев или отказа оборудования.
В языке C++ концепция ACID может использоваться при работе с базами данных, например, при использовании библиотеки SQLite. ACID гарантирует, что транзакции будут выполнены одновременно и когда они закончатся, база данных находится в согласованном состоянии.
Atomicity гарантирует, что транзакция будет выполнена как одно целое и либо полностью, либо не выполнится вообще.
Consistency гарантирует, что данные в базе будут согласованы с правилами и ограничениями базы данных.
Isolation гарантирует, что транзакции будут выполнены независимо друг от друга и не будут влиять друг на друга.
Durability гарантирует, что данные в базе данных сохранятся даже в случае сбоев или отказа оборудования.
#вопросы_с_собеседований
Что выводит код выше?
Вывод будет выглядеть так:
from A
from A
from Base
Здесь важно отметить порядок уничтожения классов и то, как Base метод возвращается к своей собственной реализации после уничтожения.
Что выводит код выше?
from A
from A
from Base
Здесь важно отметить порядок уничтожения классов и то, как Base метод возвращается к своей собственной реализации после уничтожения.
#вопросы_с_собеседований
В чем проблема с этим кодом?
Поведение не определено, потому что деструктор не является виртуальным.
Из спецификации:
( C++11 §5.3.5/3 ) - если статический тип удаляемого объекта отличается от его динамического типа, статический тип должен быть базовым классом динамического типа удаляемого объекта, а статический тип должен иметь виртуальный деструктор, иначе поведение не определено.
В чем проблема с этим кодом?
Из спецификации:
( C++11 §5.3.5/3 ) - если статический тип удаляемого объекта отличается от его динамического типа, статический тип должен быть базовым классом динамического типа удаляемого объекта, а статический тип должен иметь виртуальный деструктор, иначе поведение не определено.
Обфускация
Обфускация - это процесс преобразования исходного кода программы в нечитаемый для человека вид, но сохраняющий работоспособность программы.
Чаще всего применяется для защиты кода от несанкционированного использования, взломов и раскрытия алгоритмов работы программы. Это может быть особенно важным для программ, которые содержат ценную информацию или используются в критических системах.
Такой код использует множество макросов, которые заменяют имена переменных и функций на случайные символы. Также, для усложнения чтения кода, используется множество пустых строк и лишних пробелов.
* На изображении слева представлен обычный код, а справа - его версия после обфускации
Обфускация - это процесс преобразования исходного кода программы в нечитаемый для человека вид, но сохраняющий работоспособность программы.
Чаще всего применяется для защиты кода от несанкционированного использования, взломов и раскрытия алгоритмов работы программы. Это может быть особенно важным для программ, которые содержат ценную информацию или используются в критических системах.
Такой код использует множество макросов, которые заменяют имена переменных и функций на случайные символы. Также, для усложнения чтения кода, используется множество пустых строк и лишних пробелов.
* На изображении слева представлен обычный код, а справа - его версия после обфускации
Расширение CUDA
CUDA extension позволяет использовать функции NVIDIA CUDA в C++ коде. Оно предоставляет библиотеку из заголовочных файлов и библиотеку объектных файлов для компиляции. Преимущества использования расширения CUDA:
В данном примере мы объявляем ядро (kernel)
CUDA extension позволяет использовать функции NVIDIA CUDA в C++ коде. Оно предоставляет библиотеку из заголовочных файлов и библиотеку объектных файлов для компиляции. Преимущества использования расширения CUDA:
- возможность использования мощностей графического процессора (GPU) для ускорения вычислений;- удобный синтаксис и возможность использования функций CUDA в C++ коде;- поддержка различных операционных систем (Windows, Linux, macOS).В данном примере мы объявляем ядро (kernel)
add, которое складывает два числа и записывает результат в указатель c. Затем мы выделяем память на устройстве (GPU) для переменной dev_c, вызываем ядро add, копируем результат в переменную c на хосте (CPU) и освобождаем память, выделенную на устройстве.#вопросы_с_собеседований
Сколько раз будет выполняться этот цикл? Поясните свой ответ.
Если бы вы сказали 300, вы были бы правы, если бы i было объявлено как int. Однако, поскольку i было объявлено как unsigned char, правильным ответом будет то, что этот код приведет к бесконечному циклу.
Вот почему:
Выражение 2 * half_limit будет преобразовано в int (по правилам преобразования C++) и будет иметь значение 300. Однако, поскольку i - это беззнаковый символ, он будет пересчитан в 8-битное значение, которое, достигнув 255, переполнится (то есть вернется к 0), и цикл, таким образом, будет продолжаться вечно.
Сколько раз будет выполняться этот цикл? Поясните свой ответ.
Вот почему:
Выражение 2 * half_limit будет преобразовано в int (по правилам преобразования C++) и будет иметь значение 300. Однако, поскольку i - это беззнаковый символ, он будет пересчитан в 8-битное значение, которое, достигнув 255, переполнится (то есть вернется к 0), и цикл, таким образом, будет продолжаться вечно.
#вопросы_с_собеседований
Как можно вызвать функцию C в программе на C++?
Чтобы вызвать функцию на языке С в программе на C++, необходимо использовать директиву extern "C". Это позволяет компилятору C++ понимать синтаксис функций на языке С, а программисту написать программу, исходники которой частично написаны на С++, а частично на С.
Как можно вызвать функцию C в программе на C++?
Coroutine
Корутины - это новый механизм в языке С++, который позволяет приостанавливать выполнение функции и возобновлять его с того же места позже.
Это особенно полезно в асинхронном программировании, когда нужно выполнить длительную операцию без блокировки потока выполнения. Корутины были добавлены в язык в стандарте C++20.
Данный пример демонстрирует создание генератора чисел Фибоначчи с использованием корутин. В функции
Корутины - это новый механизм в языке С++, который позволяет приостанавливать выполнение функции и возобновлять его с того же места позже.
Это особенно полезно в асинхронном программировании, когда нужно выполнить длительную операцию без блокировки потока выполнения. Корутины были добавлены в язык в стандарте C++20.
Данный пример демонстрирует создание генератора чисел Фибоначчи с использованием корутин. В функции
fib происходит вычисление чисел Фибоначчи, при этом выполнение функции приостанавливается с помощью ключевого слова co_yield, когда нужно вернуть результат. В функции main происходит использование генератора для вывода первых 10 чисел Фибоначчи на экран.ext_aggregate
В стандарте C++17 появилась новая возможность для инициализации агрегатных объектов - ext_aggregate. Данная функциональность позволяет создавать объекты с помощью инициализации, используя списки инициализации.
В данном примере мы создаем объект типа
Ранее в C++ для инициализации агрегатных объектов использовались фигурные скобки, которые позволяли инициализировать объекты только в том порядке, в котором объявлены переменные в структуре.
ext_aggregate позволяет инициализировать переменные в любом порядке, что делает код более читабельным и гибким.
В стандарте C++17 появилась новая возможность для инициализации агрегатных объектов - ext_aggregate. Данная функциональность позволяет создавать объекты с помощью инициализации, используя списки инициализации.
В данном примере мы создаем объект типа
Person с помощью инициализации переменных через ext_aggregate. Используя точки и имена переменных, мы можем явно указать, какое значение присваивается каждой переменной.Ранее в C++ для инициализации агрегатных объектов использовались фигурные скобки, которые позволяли инициализировать объекты только в том порядке, в котором объявлены переменные в структуре.
ext_aggregate позволяет инициализировать переменные в любом порядке, что делает код более читабельным и гибким.
RPC (Remote Procedure Call)
Это технология межпроцессного взаимодействия, которая позволяет вызывать процедуры на удаленном компьютере, как если бы они были локальными.
Существует несколько библиотек для RPC в C++, включая:
-
-
-
-
Каждая из этих библиотек имеет свои особенности и применяется в разных случаях. Например,
В этом примере кода клиент использует
Это технология межпроцессного взаимодействия, которая позволяет вызывать процедуры на удаленном компьютере, как если бы они были локальными.
Существует несколько библиотек для RPC в C++, включая:
-
Apache Thrift-
gRPC-
ONC/RPC-
XML-RPC++Каждая из этих библиотек имеет свои особенности и применяется в разных случаях. Например,
Apache Thrift обычно используется для создания кросс-языковых приложений, а gRPC - для создания масштабируемых и быстрых приложений на основе протокола HTTP/2.В этом примере кода клиент использует
gRPC для вызова удаленной процедуры SayHello на сервере. Он создает объект GreeterClient, который использует gRPC для установления связи с удаленным сервером и вызова метода SayHello. Затем клиент получает ответ от сервера и выводит его на экран.SIMD (Single Instruction Multiple Data)
Это технология, позволяющая одновременно выполнять одну и ту же операцию над несколькими наборами данных. В современных процессорах SIMD-инструкции реализованы для ускорения работы с векторными операциями. В C++ SIMD-инструкции доступны через библиотеку
В данном примере функция
Это технология, позволяющая одновременно выполнять одну и ту же операцию над несколькими наборами данных. В современных процессорах SIMD-инструкции реализованы для ускорения работы с векторными операциями. В C++ SIMD-инструкции доступны через библиотеку
immintrin.hВ данном примере функция
addVectors принимает на вход указатели на три массива a, b и c, каждый из которых содержит 8 элементов типа float. С помощью функции _mm256_loadu_ps происходит загрузка первого и второго векторов в регистры, затем с помощью функции _mm256_add_ps происходит их покомпонентное сложение. Результат сохраняется в третий вектор с помощью функции _mm256_storeu_ps#вопросы_с_собеседований
Как тестировать закрытые методы?
Проверка закрытых методов в C++ может быть осуществлена путем написания тестовых случаев, которые используют публичные методы, которые в свою очередь вызывают закрытые методы. Этот подход называется "тестирование черного ящика" и позволяет тестировать функциональность закрытых методов, не раскрывая их реализацию.
Как тестировать закрытые методы?
Valgrind
Valgrind - это набор инструментов для отладки и профилирования программного обеспечения, написанного на языке C/C++. Valgrind обнаруживает ошибки в использовании памяти, утечки памяти, профилирует программы, позволяя оптимизировать их производительность.
Valgrind состоит из нескольких инструментов, каждый из которых решает свою задачу. Например,
В данном примере мы выделяем память под 10 целочисленных значений и записываем значение в ячейку с индексом 10, что приводит к чтению/записи за пределами выделенной памяти. Затем мы освобождаем выделенную память с помощью функции
Valgrind - это набор инструментов для отладки и профилирования программного обеспечения, написанного на языке C/C++. Valgrind обнаруживает ошибки в использовании памяти, утечки памяти, профилирует программы, позволяя оптимизировать их производительность.
Valgrind состоит из нескольких инструментов, каждый из которых решает свою задачу. Например,
Memcheck обнаруживает ошибки памяти, Callgrind профилирует программы, Cachegrind анализирует использование кэша процессора.В данном примере мы выделяем память под 10 целочисленных значений и записываем значение в ячейку с индексом 10, что приводит к чтению/записи за пределами выделенной памяти. Затем мы освобождаем выделенную память с помощью функции
free. Однако, ошибка не обнаруживается при компиляции и выполнении программы, но Valgrind может обнаружить ее при запуске программы.Спецификатор thread_local
Спецификатор
Это может быть полезным в ситуациях, когда необходимо иметь глобальную переменную, доступную для каждого потока, но значение этой переменной должно быть уникальным для каждого потока.
В данном примере мы создали переменную
В функции
Спецификатор
thread_local позволяет создавать переменные, которые будут иметь своё значение для каждого потока, в который они будут загружены. То есть, каждый поток будет иметь свою собственную копию этой переменной.Это может быть полезным в ситуациях, когда необходимо иметь глобальную переменную, доступную для каждого потока, но значение этой переменной должно быть уникальным для каждого потока.
В данном примере мы создали переменную
x с помощью спецификатора thread_local. Затем мы создали функцию increment(), которая инкрементирует значение переменной x и выводит его на экран.В функции
main() мы создали два потока и передали им функцию increment(). Каждый поток будет иметь свою собственную копию переменной x, поэтому при выполнении функции increment() в каждом потоке будет изменяться только своя копия переменной x. После выполнения обоих потоков мы ожидаем завершения их работы с помощью метода join().#вопросы_с_собеседований
Напишите базовую реализацию std::shared_ptr.
std::shared_ptr - умный указатель, который позволяет разделять владение объектом между несколькими shared_ptr. Когда последний shared_ptr уничтожается, он автоматически удаляет объект.
Напишите базовую реализацию std::shared_ptr.