Синхронизация между стандартными потоками C++ и C
Функция
По умолчанию, эта синхронизация включена, это означает, что потоки C++ и C могут быть использованы вместе, и их буферы будут иметь правильный порядок.
Вызов
Этот код используется для быстрого чтения и записи данных, что особенно полезно в соревновательном программировании.
Здесь мы также отвязываем
Функция
std::ios::sync_with_stdio
используется для установки синхронизации между стандартными потоками C++ и стандартными потоками C.По умолчанию, эта синхронизация включена, это означает, что потоки C++ и C могут быть использованы вместе, и их буферы будут иметь правильный порядок.
Вызов
std::ios::sync_with_stdio(false)
может увеличить производительность ввода/вывода, но после этого стандартные потоки C++ и C не должны использоваться вместе.Этот код используется для быстрого чтения и записи данных, что особенно полезно в соревновательном программировании.
Здесь мы также отвязываем
std::cin
от std::cout
, что дополнительно увеличивает скорость ввода/вывода и используем \n
вместо std::endl
, т. к. std::endl
выполняет отчиску буфера и может замедлить вывод.Флаг компиляции -fPIC
Это означает, что сгенерированный код может быть исполнен независимо от его абсолютного адреса в памяти.
Это особенно полезно при создании динамических библиотек, которые могут быть загружены в произвольное место в памяти во время выполнения.
Результатом применения этого флага будет объектный файл, который содержит позиционно-независимый код. Этот объектный файл затем может быть использован для создания динамической библиотеки, которую можно загрузить и использовать во время выполнения других программ.
-fPIC
используется для генерации позиционно-независимого кода (Position Independent Code, PIC). Это означает, что сгенерированный код может быть исполнен независимо от его абсолютного адреса в памяти.
Это особенно полезно при создании динамических библиотек, которые могут быть загружены в произвольное место в памяти во время выполнения.
Результатом применения этого флага будет объектный файл, который содержит позиционно-независимый код. Этот объектный файл затем может быть использован для создания динамической библиотеки, которую можно загрузить и использовать во время выполнения других программ.
#вопросы_с_собеседований
Что такое internal linkage?
internal linkage (внутреннее связывание) означает, что имя (например, переменная или функция) видимо и доступно только в пределах файла (или, точнее, в пределах трансляционной единицы), в котором оно определено. Это означает, что если у вас есть два разных файла с исходным кодом, и в каждом из них определено имя с внутренней связью, то эти два имени считаются разными и не конфликтуют друг с другом.
Внутреннюю связь в C++ можно установить несколькими способами. Например, если вы определите переменную или функцию как static, она будет иметь внутреннюю связь. Также, имена в безымянных пространствах имен (anonymous namespaces) имеют внутреннюю связь.
Что такое internal linkage?
Внутреннюю связь в C++ можно установить несколькими способами. Например, если вы определите переменную или функцию как static, она будет иметь внутреннюю связь. Также, имена в безымянных пространствах имен (anonymous namespaces) имеют внутреннюю связь.
Template Method
Паттерн Template Method относится к поведенческим шаблонам проектирования и предоставляет скелет алгоритма в базовом классе, позволяя подклассам переопределять некоторые шаги алгоритма без изменения его структуры.
Этот паттерн часто используется в разработке фреймворков, где каждый подкласс реализует неизменные части архитектуры домена, оставляя "заполнители" для опций настройки.
В этом примере
Паттерн Template Method относится к поведенческим шаблонам проектирования и предоставляет скелет алгоритма в базовом классе, позволяя подклассам переопределять некоторые шаги алгоритма без изменения его структуры.
Этот паттерн часто используется в разработке фреймворков, где каждый подкласс реализует неизменные части архитектуры домена, оставляя "заполнители" для опций настройки.
В этом примере
AbstractClass
определяет шаблонный метод TemplateMethod()
, который состоит из вызовов различных операций в определенной последовательности. Некоторые из этих операций делегируются подклассам ConcreteClass1
и ConcreteClass2
.Aggregate initialization
Aggregate initialization — это форма инициализации, которая позволяет инициализировать агрегаты (объекты определенных типов) с использованием фигурных скобок и списка значений. Агрегаты могут быть одним из следующих типов:
В этом примере мы создаем структуру
Aggregate initialization — это форма инициализации, которая позволяет инициализировать агрегаты (объекты определенных типов) с использованием фигурных скобок и списка значений. Агрегаты могут быть одним из следующих типов:
-
Массивы-
Структуры или классы без пользовательских конструкторов, без закрытых или защищенных нестатических членов данных, без базовых классов и без виртуальных функций.В этом примере мы создаем структуру
Point
, которая содержит два целочисленных поля x
и y
. Затем мы инициализируем объект p1
этой структуры с помощью aggregate initialization, указывая значения для x
и y
в фигурных скобках. Аналогично, мы инициализируем массив arr с помощью списка значений в фигурных скобках.Uniform initialization
Uniform initialization — это способ инициализации переменных и объектов, который был введен в стандарте C++11. Он представляет собой универсальный и более предсказуемый способ инициализации, который использует фигурные скобки
Преимущества uniform initialization включают:
*Важно отметить, что uniform initialization не всегда работает так, как ожидается, особенно в случае с перегруженными конструкторами. В некоторых случаях, компилятор может выбрать не тот конструктор, который вы ожидали, что может привести к неожиданному поведению.
Uniform initialization — это способ инициализации переменных и объектов, который был введен в стандарте C++11. Он представляет собой универсальный и более предсказуемый способ инициализации, который использует фигурные скобки
{}
вместо круглых ()
или присваивания =
.Преимущества uniform initialization включают:
-
Предотвращает узкое преобразование (narrowing conversion), которое может привести к потере данных.-
Обеспечивает одинаковый синтаксис для инициализации всех типов данных и структур.-
Позволяет инициализировать объекты, которые ранее не могли быть инициализированы, такие как массивы и структуры.*Важно отметить, что uniform initialization не всегда работает так, как ожидается, особенно в случае с перегруженными конструкторами. В некоторых случаях, компилятор может выбрать не тот конструктор, который вы ожидали, что может привести к неожиданному поведению.
cereal
Библиотека
В этом примере мы создаем структуру
Библиотека
cereal
для C++ — это гибкая и эффективная библиотека для сериализации, которая поддерживает множество форматов, включая бинарные, XML и JSON. Она может быть использована в различных областях, где требуется сохранение и восстановление состояния объектов. В этом примере мы создаем структуру
MyData
и сохраняем ее состояние в файле с помощью cereal::BinaryOutputArchive
. Затем мы восстанавливаем структуру из этого файла с помощью cereal::BinaryInputArchive
. В конце мы выводим значения полей m2
, чтобы убедиться, что состояние было правильно восстановлено.Boost.Serialization
В этом примере мы создаем объект
Boost.Serialization
— это часть обширной библиотеки Boost
и предоставляет мощные возможности для сериализации и десериализации объектов. Она поддерживает большинство типов данных C++, включая пользовательские типы, и может сериализовать данные в различные форматы, включая двоичный, текстовый и XML.Boost.Serialization
может использоваться для сохранения состояния программы, передачи данных между процессами и постоянного хранения данных.В этом примере мы создаем объект
MyClass
с именем obj1
и сохраняем его состояние в файле с помощью boost::archive::text_oarchive
. Затем мы восстанавливаем объект obj2
из этого файла с помощью boost::archive::text_iarchive
. В конце мы выводим значения полей obj2
, чтобы убедиться, что состояние было правильно восстановлено.#вопросы_с_собеседований
Что такое cache miss и как это выявить?
Cache miss — это событие, когда система или приложение делает запрос на получение данных из кэша, но эти данные в данный момент отсутствуют в кэш-памяти. В результате системе или приложению приходится делать вторую попытку найти данные, на этот раз в более медленной основной базе данных.
Чтобы выявить cache miss в C++, вы можете использовать инструменты профилирования, которые могут анализировать события, связанные с кэшем. Некоторые из таких инструментов включают:
Что такое cache miss и как это выявить?
Cache miss — это событие, когда система или приложение делает запрос на получение данных из кэша, но эти данные в данный момент отсутствуют в кэш-памяти. В результате системе или приложению приходится делать вторую попытку найти данные, на этот раз в более медленной основной базе данных.
Чтобы выявить cache miss в C++, вы можете использовать инструменты профилирования, которые могут анализировать события, связанные с кэшем. Некоторые из таких инструментов включают:
Cachegrind
: инструмент для профилирования кэша, который является частью набора инструментов Valgrind
. Cachegrind
может анализировать поведение кэша вашего приложения и предоставлять информацию о cache miss и других событиях, связанных с кэшем.Perf
: инструмент для профилирования производительности в Linux, который может использовать аппаратные счетчики процессора для анализа событий, связанных с кэшем.OProfile
: еще один инструмент для профилирования производительности в Linux, который также может использовать аппаратные счетчики процессора для анализа событий, связанных с кэшем.std::atomic
В этом примере у нас есть глобальная переменная
std::atomic
является частью библиотеки <atomic>, которая была введена в C++11 для поддержки операций с атомарностью. Атомарные операции гарантируют, что операции будут выполнены как единое, неделимое действие, что особенно важно в многопоточном программировании, чтобы избежать состояний гонки.std::atomic
может быть использован в любом месте, где требуется безопасность потоков, например, при обновлении глобальных или общих переменных в многопоточной среде.В этом примере у нас есть глобальная переменная
counter
, которую мы хотим инкрементировать в двух разных потоках. Без использования std::atomic
мы могли бы столкнуться с состоянием гонки (race condition), когда оба потока пытаются обновить counter
одновременно. Однако, поскольку мы используем std::atomic<int>
, каждое обновление counter является атомарной операцией, и состояние гонки не происходит.Ключевое слово friend
В этом примере чтобы предоставить функции
friend
в C++ используется для предоставления доступа к закрытым (private) и защищенным (protected) членам класса другим классам или функциям. Это позволяет создавать более гибкие и тесные взаимодействия между классами или функциями, не нарушая инкапсуляцию.friend
может применяться к функциям или классам. Если функция объявлена как friend
класса, она получает доступ ко всем закрытым и защищенным членам этого класса. Если класс объявлен как friend
другого класса, все его методы получают доступ к закрытым и защищенным членам другого класса.В этом примере чтобы предоставить функции
printVolume
доступ к закрытым членам класса Box
, мы объявляем ее дружественной функцией с помощью ключевого слова friend
. Теперь функция printVolume
может обращаться к закрытым членам класса Box
и вычислять объем коробки.std::runtime_error
В данном случае, переменная
std::runtime_error
является классом исключений в стандартной библиотеке C++, который наследуется от класса std::exception
. Этот класс предназначен для представления ошибок, которые обнаруживаются во время выполнения программы. std::runtime_error
обычно используется для создания пользовательских исключений, которые могут возникнуть из-за ошибок во время выполнения, таких как некорректные аргументы функции, неправильная работа с памятью или другие ошибки, которые нельзя обнаружить на этапе компиляции.В данном случае, переменная
b
равна нулю, поэтому при вызове функции divide
будет выброшено исключение. В блоке catch
мы перехватываем исключение и выводим сообщение об ошибке.#вопросы_с_собеседований
Как запретить наследовать класс?
Чтобы запретить наследование класса в C++, вы можете объявить класс как
Ключевое слово
Как запретить наследовать класс?
Чтобы запретить наследование класса в C++, вы можете объявить класс как
final
. Ключевое слово
final
указывает компилятору, что класс не может быть использован в качестве базового класса для других классов. Если попытаться наследовать от класса, объявленного как final
, компилятор выдаст ошибку.class Base final {
// ...
};
class Derived : public Base {
// ...
};
// Ошибка компиляции: класс Base объявлен как final
Pure virtual function
Чисто виртуальная функция объявляется путем присваивания в объявлении.
В этом примере у нас есть абстрактный базовый класс
Затем мы создаем два производных класса
В функции
Pure virtual function
(чисто виртуальная функция) — это виртуальная функция, для которой мы можем иметь реализацию, но мы должны переопределить эту функцию в производном классе, иначе производный класс также станет абстрактным классом. Чисто виртуальная функция объявляется путем присваивания в объявлении.
В этом примере у нас есть абстрактный базовый класс
Shape
, который содержит чисто виртуальную функцию draw()
. Мы не можем создать объект класса Shape
, так как он является абстрактным классом. Затем мы создаем два производных класса
Circle
и Rectangle
, которые переопределяют функцию draw()
. В функции
main()
мы создаем объекты circle
и rectangle
, а также указатель на базовый класс Shape
. Мы устанавливаем указатель на объекты circle
и rectangle
и вызываем функцию draw()
для каждого из них.Ленивые вычисления в С++
Ленивые вычисления могут быть реализованы с использованием различных подходов, таких как отложенное вычисление (
В этом примере вычисления выполняются только при первом обращении к числу Фибоначчи с определенным индексом, после чего результат сохраняется в контейнере
lazy evaluation
— это стратегия вычислений, при которой вычисления откладываются до тех пор, пока результат не станет действительно необходимым. Это позволяет оптимизировать производительность и ресурсоемкость программы, избегая ненужных вычислений.Ленивые вычисления могут быть реализованы с использованием различных подходов, таких как отложенное вычисление (
deferred evaluation
), вычисление по требованию (on-demand evaluation
) и мемоизация (memoization
).В этом примере вычисления выполняются только при первом обращении к числу Фибоначчи с определенным индексом, после чего результат сохраняется в контейнере
memo
для последующего использования. Это позволяет избежать повторных вычислений и оптимизировать производительность программы.#вопросы_с_собеседований
Как работает RTTI?
RTTI расшифровывается как "Run-Time Type Information" и является функцией в C++, которая предоставляет информацию о типе объекта во время выполнения. Она позволяет вам динамически запрашивать и манипулировать информацией о типе объекта.
В C++ информация о типе объектов обычно представлена механизмом, называемым классом type_info. Класс type_info является частью стандартной библиотеки C++ и определяется в заголовке <typeinfo>.
Чтобы использовать RTTI в C++, необходимо включить функцию RTTI, указав флаг компилятора -frtti или включив его в настройках проекта.
RTTI особенно полезен, когда у вас есть указатель на базовый класс или ссылка на объект производного класса. RTTI обеспечивает безопасную передачу указателя базового класса в производный класс и выполнение определенных операций. Для этого используется оператор dynamic_cast, который выполняет динамическое приведение и возвращает указатель или ссылку целевого типа, если приведение корректно, или нулевой указатель, или выбрасывает исключение, если приведение не удалось.
Как работает RTTI?
В C++ информация о типе объектов обычно представлена механизмом, называемым классом type_info. Класс type_info является частью стандартной библиотеки C++ и определяется в заголовке <typeinfo>.
Чтобы использовать RTTI в C++, необходимо включить функцию RTTI, указав флаг компилятора -frtti или включив его в настройках проекта.
RTTI особенно полезен, когда у вас есть указатель на базовый класс или ссылка на объект производного класса. RTTI обеспечивает безопасную передачу указателя базового класса в производный класс и выполнение определенных операций. Для этого используется оператор dynamic_cast, который выполняет динамическое приведение и возвращает указатель или ссылку целевого типа, если приведение корректно, или нулевой указатель, или выбрасывает исключение, если приведение не удалось.
#вопросы_с_собеседований
Что такое copy elision и когда становится возможным? Какие особенности для разных стандартов?
copy elision - это техника оптимизации компилятора в C++, которая позволяет устранить ненужные операции копирования или перемещения при возврате объектов из функций или инициализации объектов. Это позволяет компилятору оптимизировать создание и уничтожение временных объектов, в результате чего код становится более эффективным.
copy elision становится возможной в сценариях, определенных стандартом C++. Соответствующее положение стандарта называется правилом "as-if", которое позволяет компилятору оптимизировать программу до тех пор, пока она производит такое же наблюдаемое поведение, как и исходный код.
- C++98/03: copy elision не является обязательной, но разрешена в качестве оптимизации. RVO* и NRVO* - обычные оптимизации, выполняемые компиляторами. *(Return Value Optimization, Named Return Value Optimization)
- C++11: Правила copy elision были пересмотрены. RVO и NRVO стали обязательными в некоторых случаях. copy elision также может происходить при выбросе исключений.
- C++17: Правила исключения копирования были еще более смягчены. Именованные переменные могут быть созданы или присвоены без необходимости выполнения операции перемещения. Эта оптимизация называется "mandatory copy elision".
- C++20: Правила исключения копирования остались такими же, как и в C++17.
Важно отметить, что хотя копирование является широко поддерживаемой оптимизацией, оно все еще зависит от реализации компилятором. Компиляторы могут иметь различное поведение или ограничения в отношении copy elision. Поэтому её использование для оптимизации поведения или производительности может быть непереносимым в различных компиляторах или версиях.
Что такое copy elision и когда становится возможным? Какие особенности для разных стандартов?
copy elision становится возможной в сценариях, определенных стандартом C++. Соответствующее положение стандарта называется правилом "as-if", которое позволяет компилятору оптимизировать программу до тех пор, пока она производит такое же наблюдаемое поведение, как и исходный код.
- C++98/03: copy elision не является обязательной, но разрешена в качестве оптимизации. RVO* и NRVO* - обычные оптимизации, выполняемые компиляторами. *(Return Value Optimization, Named Return Value Optimization)
- C++11: Правила copy elision были пересмотрены. RVO и NRVO стали обязательными в некоторых случаях. copy elision также может происходить при выбросе исключений.
- C++17: Правила исключения копирования были еще более смягчены. Именованные переменные могут быть созданы или присвоены без необходимости выполнения операции перемещения. Эта оптимизация называется "mandatory copy elision".
- C++20: Правила исключения копирования остались такими же, как и в C++17.
Важно отметить, что хотя копирование является широко поддерживаемой оптимизацией, оно все еще зависит от реализации компилятором. Компиляторы могут иметь различное поведение или ограничения в отношении copy elision. Поэтому её использование для оптимизации поведения или производительности может быть непереносимым в различных компиляторах или версиях.
Алгоритм fill_n
Алгоритм используется для заполнения некоторых значений по умолчанию в контейнере.
Он принимает начало итератора и количество позиций n в качестве аргументов и заполняет первую позицию n , начиная с позиции, указанной параметром begin , заданным значением.
Синтаксис:
Алгоритм используется для заполнения некоторых значений по умолчанию в контейнере.
Он принимает начало итератора и количество позиций n в качестве аргументов и заполняет первую позицию n , начиная с позиции, указанной параметром begin , заданным значением.
Синтаксис:
void fill_n (начало итератора, int n, значение типа);