Задача
В текстовом файле, содержащем текст программы на языке Си, проверить соответствие открывающихся и закрывающихся фигурных скобок { и }. Результат проверки вывести на экран и записать в виде фразы в текстовый файл. Результат работы программы (вывод) поместить в отдельный текстовый файл (например, «out . txt " ), продублировав на экране.
В текстовом файле, содержащем текст программы на языке Си, проверить соответствие открывающихся и закрывающихся фигурных скобок { и }. Результат проверки вывести на экран и записать в виде фразы в текстовый файл. Результат работы программы (вывод) поместить в отдельный текстовый файл (например, «out . txt " ), продублировав на экране.
Что такое выравнивание данных?
Выравнивание данных (data alignment) — это процесс выравнивания слов памяти в компьютерной системе таким образом, чтобы каждый адрес начала слова был кратен адресу выравнивания для этого слова. Это делается для увеличения производительности, так как доступ к памяти, выровненной по границе слова, выполняется быстрее, чем к памяти с не выровненным доступом.
Большинство компиляторов C++ пытаются генерировать код, который соответствует некоторым правилам выравнивания памяти на конкретном аппаратном обеспечении.
Выравнивание данных (data alignment) — это процесс выравнивания слов памяти в компьютерной системе таким образом, чтобы каждый адрес начала слова был кратен адресу выравнивания для этого слова. Это делается для увеличения производительности, так как доступ к памяти, выровненной по границе слова, выполняется быстрее, чем к памяти с не выровненным доступом.
Большинство компиляторов C++ пытаются генерировать код, который соответствует некоторым правилам выравнивания памяти на конкретном аппаратном обеспечении.
Поиск в ширину
Поиск в ширину (breadth-first search, BFS) — это алгоритм поиска или обхода графа. Он исследует все вершины на одном уровне, прежде чем переходить к следующему уровню.
Этот пример иллюстрирует обход в ширину для следующего графа:
0
/ \
1-----2
\
3
Начиная с вершины 2, алгоритм BFS посетит вершины в следующем порядке: 2, 0, 3, 1.
Поиск в ширину (breadth-first search, BFS) — это алгоритм поиска или обхода графа. Он исследует все вершины на одном уровне, прежде чем переходить к следующему уровню.
Этот пример иллюстрирует обход в ширину для следующего графа:
0
/ \
1-----2
\
3
Начиная с вершины 2, алгоритм BFS посетит вершины в следующем порядке: 2, 0, 3, 1.
Есть ли разница между классом и структурой?
Единственная разница между классом и структурой — это модификаторы доступа. Члены структуры по умолчанию public; члены класса являются приватными. Рекомендуется использовать классы, когда вам нужен объект с методами, и структуры, когда у вас есть простой объект данных.
Единственная разница между классом и структурой — это модификаторы доступа. Члены структуры по умолчанию public; члены класса являются приватными. Рекомендуется использовать классы, когда вам нужен объект с методами, и структуры, когда у вас есть простой объект данных.
prev_permutation
prev_permutation — это стандартный алгоритм в C++, который используется для генерации предыдущей перестановки элементов в контейнере, таком как вектор, строка или массив. Этот алгоритм изменяет порядок элементов в контейнере на предыдущий лексикографический порядок, если это возможно.
Обратите внимание, что prev_permutation изменяет сам контейнер, поэтому оригинальный вектор vec будет содержать предыдущую перестановку после вызова этой функции.
prev_permutation — это стандартный алгоритм в C++, который используется для генерации предыдущей перестановки элементов в контейнере, таком как вектор, строка или массив. Этот алгоритм изменяет порядок элементов в контейнере на предыдущий лексикографический порядок, если это возможно.
Обратите внимание, что prev_permutation изменяет сам контейнер, поэтому оригинальный вектор vec будет содержать предыдущую перестановку после вызова этой функции.
std::lock_guard
std::lock_guard — это один из классов в стандартной библиотеке C++, предназначенных для обеспечения многопоточной безопасности при работе с разделяемыми данными. Он используется для автоматического захвата и освобождения мьютекса (mutex) при входе и выходе из блока кода.
Прежде всего, мьютекс (mutex) — это механизм синхронизации, который предоставляет эксклюзивный доступ к разделяемым данным одному потоку в данный момент времени. std::lock_guard является удобной оберткой для работы с мьютексами.
std::lock_guard — это один из классов в стандартной библиотеке C++, предназначенных для обеспечения многопоточной безопасности при работе с разделяемыми данными. Он используется для автоматического захвата и освобождения мьютекса (mutex) при входе и выходе из блока кода.
Прежде всего, мьютекс (mutex) — это механизм синхронизации, который предоставляет эксклюзивный доступ к разделяемым данным одному потоку в данный момент времени. std::lock_guard является удобной оберткой для работы с мьютексами.
Лямбда-функция
Лямбда-функция (или просто лямбда) — это анонимная функция в C++, которая может быть определена непосредственно внутри кода. Лямбда-функции предоставляют более компактный и удобный способ создания небольших функций на лету, без необходимости объявления их отдельно.
Лямбда-функции также часто используются вместе с алгоритмами стандартной библиотеки C++, такими как std::for_each, std::transform, std::sort (как на примере выше), и другими, чтобы создавать более компактный и выразительный код.
Лямбда-функция (или просто лямбда) — это анонимная функция в C++, которая может быть определена непосредственно внутри кода. Лямбда-функции предоставляют более компактный и удобный способ создания небольших функций на лету, без необходимости объявления их отдельно.
Лямбда-функции также часто используются вместе с алгоритмами стандартной библиотеки C++, такими как std::for_each, std::transform, std::sort (как на примере выше), и другими, чтобы создавать более компактный и выразительный код.
std::bitset
std::bitset — это шаблон класса, который представляет собой последовательность битов фиксированного размера N. Битовые множества могут управляться стандартными логическими операторами и преобразовываться в строки и целые числа.
В этом примере мы создаем битовое множество b размером 8 бит и инициализируем его значением 42. Затем мы выводим его на экран. Результат работы этого кода: 00101010.
std::bitset — это шаблон класса, который представляет собой последовательность битов фиксированного размера N. Битовые множества могут управляться стандартными логическими операторами и преобразовываться в строки и целые числа.
В этом примере мы создаем битовое множество b размером 8 бит и инициализируем его значением 42. Затем мы выводим его на экран. Результат работы этого кода: 00101010.
Функция memmove
Функция memmove в C++ используется для перемещения блока памяти из одной части массива в другую, даже если эти блоки памяти перекрываются. Это отличается от функции memcpy, которая не гарантирует правильное копирование, если исходный и целевой блоки перекрываются.
Прототип функции memmove выглядит следующим образом:
void* memmove(void* destination, const void* source, size_t num);
Здесь:
destination — указатель на начало блока памяти, в который нужно переместить данные.
source — указатель на начало блока памяти, из которого нужно скопировать данные.
num — количество байтов, которые нужно переместить.
Функция memmove обеспечивает корректное копирование данных, даже если destination и source перекрываются. Это означает, что она может быть использована в случаях, когда memcpy может привести к неправильным результатам из-за перекрытия.
Функция memmove в C++ используется для перемещения блока памяти из одной части массива в другую, даже если эти блоки памяти перекрываются. Это отличается от функции memcpy, которая не гарантирует правильное копирование, если исходный и целевой блоки перекрываются.
Прототип функции memmove выглядит следующим образом:
void* memmove(void* destination, const void* source, size_t num);
Здесь:
destination — указатель на начало блока памяти, в который нужно переместить данные.
source — указатель на начало блока памяти, из которого нужно скопировать данные.
num — количество байтов, которые нужно переместить.
Функция memmove обеспечивает корректное копирование данных, даже если destination и source перекрываются. Это означает, что она может быть использована в случаях, когда memcpy может привести к неправильным результатам из-за перекрытия.
Побитовое копирование
Побитовое копирование — это процесс создания копии объекта, при котором все биты исходного объекта копируются в целевой объект. Это означает, что все члены данных исходного объекта копируются в целевой объект, включая указатели, массивы и структуры.
В C++ побитовое копирование выполняется конструктором копирования. Если конструктор копирования не определен для класса, компилятор генерирует его неявно. Генерируемый компилятором конструктор копирования выполняет побитовое копирование всех членов данных класса.
Побитовое копирование — это процесс создания копии объекта, при котором все биты исходного объекта копируются в целевой объект. Это означает, что все члены данных исходного объекта копируются в целевой объект, включая указатели, массивы и структуры.
В C++ побитовое копирование выполняется конструктором копирования. Если конструктор копирования не определен для класса, компилятор генерирует его неявно. Генерируемый компилятором конструктор копирования выполняет побитовое копирование всех членов данных класса.
Спецификаторы доступа
В C++ используются три спецификатора доступа: public, private и protected. Они определяют, кто может получить доступ к членам класса или структуры.
public — члены с таким спецификатором доступны из любого места программы, включая другие классы и структуры.
private — члены с таким спецификатором доступны только из самого класса, в котором они объявлены.
protected — члены с таким спецификатором доступны из самого класса, в котором они объявлены, а также из производных классов.
По умолчанию все члены класса объявляются с спецификатором private, а члены структуры — с public.
Спецификаторы доступа используются для обеспечения инкапсуляции, то есть отделения внутренней реализации класса от его интерфейса. Инкапсуляция позволяет скрыть детали реализации от пользователя класса, что делает код более понятным и надежным.
В C++ используются три спецификатора доступа: public, private и protected. Они определяют, кто может получить доступ к членам класса или структуры.
public — члены с таким спецификатором доступны из любого места программы, включая другие классы и структуры.
private — члены с таким спецификатором доступны только из самого класса, в котором они объявлены.
protected — члены с таким спецификатором доступны из самого класса, в котором они объявлены, а также из производных классов.
По умолчанию все члены класса объявляются с спецификатором private, а члены структуры — с public.
Спецификаторы доступа используются для обеспечения инкапсуляции, то есть отделения внутренней реализации класса от его интерфейса. Инкапсуляция позволяет скрыть детали реализации от пользователя класса, что делает код более понятным и надежным.
Функция floor
Функция floor в C++ используется для округления числа с плавающей запятой (типа float или double) вниз до ближайшего целого числа, которое меньше или равно исходному числу. Функция floor является частью стандартной библиотеки C++ и объявлена в заголовочном файле cmath.
Функция floor полезна, например, при работе с денежными суммами, когда вам нужно округлить результат вниз до ближайшего целого значения валюты, чтобы учесть минимальные единицы валюты.
Функция floor в C++ используется для округления числа с плавающей запятой (типа float или double) вниз до ближайшего целого числа, которое меньше или равно исходному числу. Функция floor является частью стандартной библиотеки C++ и объявлена в заголовочном файле cmath.
Функция floor полезна, например, при работе с денежными суммами, когда вам нужно округлить результат вниз до ближайшего целого значения валюты, чтобы учесть минимальные единицы валюты.
🧑💻 Статьи для IT: как объяснять и распространять значимые идеи
Напоминаем, что у нас есть бесплатный курс для всех, кто хочет научиться интересно писать — о программировании и в целом.
Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций.
Для кого: для авторов, копирайтеров и просто программистов, которые хотят научиться интересно рассказывать о своих проектах.
👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.
Напоминаем, что у нас есть бесплатный курс для всех, кто хочет научиться интересно писать — о программировании и в целом.
Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций.
Для кого: для авторов, копирайтеров и просто программистов, которые хотят научиться интересно рассказывать о своих проектах.
👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.
Флаги оптимизации
🤔 Что это за флажки?
Флажки оптимизации — это специальные параметры, которые передают компилятору инструкции о том, насколько сильно он должен оптимизировать ваш код при компиляции. Уровень оптимизации может варьироваться от минимального (O0) до максимального (O3).
🔍 Чем отличаются уровни оптимизации?
• O0 (Отсутствие оптимизации): Этот уровень подходит для отладки. Компилятор минимизирует время компиляции, не внося оптимизаций, чтобы облегчить отслеживание багов
• O1 (Базовая оптимизация): Проводит базовые оптимизации, например, устранение мертвого кода и простые inline-функций
• O2 (Средняя оптимизация): Включает более сложные оптимизации, такие как оптимизация циклов и векторизация, что приводит к более эффективному исполнению кода
• O3 (Максимальная оптимизация): Производит максимально возможное количество оптимизаций. Подходит для создания высокопроизводительного кода, но может увеличить время компиляции
🤔 Что это за флажки?
Флажки оптимизации — это специальные параметры, которые передают компилятору инструкции о том, насколько сильно он должен оптимизировать ваш код при компиляции. Уровень оптимизации может варьироваться от минимального (O0) до максимального (O3).
🔍 Чем отличаются уровни оптимизации?
• O0 (Отсутствие оптимизации): Этот уровень подходит для отладки. Компилятор минимизирует время компиляции, не внося оптимизаций, чтобы облегчить отслеживание багов
• O1 (Базовая оптимизация): Проводит базовые оптимизации, например, устранение мертвого кода и простые inline-функций
• O2 (Средняя оптимизация): Включает более сложные оптимизации, такие как оптимизация циклов и векторизация, что приводит к более эффективному исполнению кода
• O3 (Максимальная оптимизация): Производит максимально возможное количество оптимизаций. Подходит для создания высокопроизводительного кода, но может увеличить время компиляции
Вывод типа шаблона (Template Type Deduction)
⚙️ Начиная с C++11 появилась возможность переложить вывод типа на компилятор — вывод типа шаблона (Template Type Deduction)
🔥 Эта фича не только улучшает читаемость, но и делает код более устойчивым к изменениям. Если мы решим изменить тип вектора, нам не нужно изменять сигнатуру функции
⚙️ Начиная с C++11 появилась возможность переложить вывод типа на компилятор — вывод типа шаблона (Template Type Deduction)
🔥 Эта фича не только улучшает читаемость, но и делает код более устойчивым к изменениям. Если мы решим изменить тип вектора, нам не нужно изменять сигнатуру функции
Какие конструкторы и методы реализуются по умолчанию?
Начиная с C++11 каждый класс, помимо конструктора по умолчанию, имеет следующие 5 дефолтных операций:
1. Конструктор копирования (Copy Constructor):
• Принимает объект своего же типа в качестве параметра
• Создает новый объект, инициализируя его значениями из переданного объекта
2. Оператор присваивания (Copy Assignment Operator):
• Присваивает значения одного объекта другому
• Вызывается при использовании оператора присваивания (=) между двумя объектами
3. Конструктор перемещения (Move Constructor):
• Это нововведение в C++11
• Принимает rvalue-ссылку на объект своего типа в качестве параметра
4. Оператор перемещения (Move Assignment Operator):
• Также нововведение в C++11
• Принимает rvalue-ссылку на объект своего типа в качестве параметра
5. Деструктор (Destructor):
• Освобождает ресурсы, занимаемые объектом, при его уничтожении (выходе из области видимости, удалении из контейнера)
Начиная с C++11 каждый класс, помимо конструктора по умолчанию, имеет следующие 5 дефолтных операций:
1. Конструктор копирования (Copy Constructor):
• Принимает объект своего же типа в качестве параметра
• Создает новый объект, инициализируя его значениями из переданного объекта
2. Оператор присваивания (Copy Assignment Operator):
• Присваивает значения одного объекта другому
• Вызывается при использовании оператора присваивания (=) между двумя объектами
3. Конструктор перемещения (Move Constructor):
• Это нововведение в C++11
• Принимает rvalue-ссылку на объект своего типа в качестве параметра
4. Оператор перемещения (Move Assignment Operator):
• Также нововведение в C++11
• Принимает rvalue-ссылку на объект своего типа в качестве параметра
5. Деструктор (Destructor):
• Освобождает ресурсы, занимаемые объектом, при его уничтожении (выходе из области видимости, удалении из контейнера)
📚 std::exception — это базовый класс для всех стандартных исключений в C++
Почему стоит наследоваться от std::exception?
• Единообразие в обработке исключений: Когда вы наследуетесь от std::exception, ваш класс исключения приобретает интерфейс, который делает его совместимым с другими стандартными исключениями
• what() метод: std::exception предоставляет важный метод what(), который возвращает строковое представление исключения. Это позволяет вам предоставлять информативные сообщения об ошибке при обработке исключений
• Легкость в поддержке кода: Если вы используете сторонние библиотеки или фреймворки, они также могут ожидать обработку исключений, производных от std::exception
• Стандартные типы исключений: std::exception имеет несколько стандартных подклассов, таких как std::runtime_error, std::logic_error и другие. Вы можете использовать эти подклассы вместо базового std::exception, чтобы более точно определить характер ошибки
Почему стоит наследоваться от std::exception?
• Единообразие в обработке исключений: Когда вы наследуетесь от std::exception, ваш класс исключения приобретает интерфейс, который делает его совместимым с другими стандартными исключениями
• what() метод: std::exception предоставляет важный метод what(), который возвращает строковое представление исключения. Это позволяет вам предоставлять информативные сообщения об ошибке при обработке исключений
• Легкость в поддержке кода: Если вы используете сторонние библиотеки или фреймворки, они также могут ожидать обработку исключений, производных от std::exception
• Стандартные типы исключений: std::exception имеет несколько стандартных подклассов, таких как std::runtime_error, std::logic_error и другие. Вы можете использовать эти подклассы вместо базового std::exception, чтобы более точно определить характер ошибки
🔍 Что такое std::thread?
std::thread предоставляет собой интерфейс для взаимодействия с системными потоками
💡Что можно запустить?
• Любой функтор можно превратить в поток
💡Что можно передать при запуске потока?
• Можно передать любое количество параметров, но стоит учитывать, что все они будут копироваться
• Чтобы передать ссылку, её стоит обернуть в std::ref или std::cref
💡Что можно сделать с потоком?
• После создания потока мы можем с ним делать две вещи: вызвать join и ждать завершение потока или вызвать detach, тогда поток просто будет работать (пока сам не завершится)
💡Начиная с C++20 появился std::jthread
• Это тоже самое, но в деструкторе будет вызываться join
std::thread предоставляет собой интерфейс для взаимодействия с системными потоками
💡Что можно запустить?
• Любой функтор можно превратить в поток
💡Что можно передать при запуске потока?
• Можно передать любое количество параметров, но стоит учитывать, что все они будут копироваться
• Чтобы передать ссылку, её стоит обернуть в std::ref или std::cref
💡Что можно сделать с потоком?
• После создания потока мы можем с ним делать две вещи: вызвать join и ждать завершение потока или вызвать detach, тогда поток просто будет работать (пока сам не завершится)
💡Начиная с C++20 появился std::jthread
• Это тоже самое, но в деструкторе будет вызываться join
Dependency Injection
Dependency Injection (DI) — это паттерн проектирования, который помогает управлять зависимостями в приложениях. Он особенно важен в объектно-ориентированных языках программирования, таких как C++, где классы и объекты играют центральную роль.
DI предполагает, что зависимости (например, объекты других классов, которые класс использует) должны передаваться в класс извне, а не создаваться им самостоятельно. Это делает класс более независимым и более тестируемым. В C++, DI можно реализовать следующими способами: внедрение через конструктор, внедрение через метод и использование фабрик. В картинке с примером мы используем внедрение через конструктор, так как это самый распространенный способ DI в C++. В конструкторе класса вы передаете зависимости как параметры.
Использование DI в C++ способствует лучшей организации кода, более простой поддержке и тестированию. Он позволяет избегать жестких зависимостей и делает ваш код более гибким и расширяемым.
Dependency Injection (DI) — это паттерн проектирования, который помогает управлять зависимостями в приложениях. Он особенно важен в объектно-ориентированных языках программирования, таких как C++, где классы и объекты играют центральную роль.
DI предполагает, что зависимости (например, объекты других классов, которые класс использует) должны передаваться в класс извне, а не создаваться им самостоятельно. Это делает класс более независимым и более тестируемым. В C++, DI можно реализовать следующими способами: внедрение через конструктор, внедрение через метод и использование фабрик. В картинке с примером мы используем внедрение через конструктор, так как это самый распространенный способ DI в C++. В конструкторе класса вы передаете зависимости как параметры.
Использование DI в C++ способствует лучшей организации кода, более простой поддержке и тестированию. Он позволяет избегать жестких зависимостей и делает ваш код более гибким и расширяемым.