Skip to content

Latest commit

 

History

History
804 lines (643 loc) · 82.7 KB

middle.md

File metadata and controls

804 lines (643 loc) · 82.7 KB

Middle

  • Общее

    1. Какие курсы прошли или книги прочитали за этот год? Чему научились?

    • Ответ:
      • Вы сами должны ответить на этот вопрос

    2. Что нравится и не нравится в С++? Чего не хватает?

    • Ответ:
      • C++ является мощным и популярным языком программирования, который имеет множество преимуществ, но также имеет свои недостатки.

      • Некоторые из преимуществ C++:

        1. Высокая скорость выполнения: C++ является компилируемым языком, который обеспечивает высокую скорость выполнения программ.
        2. Низкоуровневый контроль: C++ предоставляет разработчикам тонкий контроль над использованием памяти и другими системными ресурсами.
        3. Объектно-ориентированный подход: C++ поддерживает объектно-ориентированное программирование, что облегчает организацию и управление сложными приложениями.
        4. Большое количество библиотек: для C++ существует огромное количество библиотек, которые упрощают разработку и ускоряют решение многих задач.
      • Однако у C++ также есть свои недостатки:

        1. Сложность: C++ является сложным языком с большим количеством особенностей и тонкостей, которые могут затруднить изучение и использование языка.
        2. Отсутствие сборщика мусора: в C++ нет автоматического управления памятью, поэтому разработчикам приходится самостоятельно следить за выделением и освобождением памяти.
        3. Неопределенное поведение: в C++ есть множество ситуаций, которые могут привести к неопределенному поведению программы, что может затруднить отладку и поиск ошибок.
      • Что касается отсутствующих функций в C++, то многие разработчики хотели бы видеть в языке более современные возможности, такие как лямбда-выражения, типы-суммы и улучшенную поддержку обобщенного программирования. Однако C++ продолжает развиваться, и многие из этих функций могут быть добавлены в будущих версиях языка. 😊

    3. Что интересного нашли в новых стандартах С++17, С++20, С++23 (конкретные фичи)?

    • Ответ:
      • C++17, C++20 и C++23 - это последние версии стандарта ISO / IEC 14882 для языка программирования C++. Каждая из этих версий включает множество новых функций и улучшений.

      • Некоторые из ключевых функций C++17:

        1. std::variant, std::optional и std::any - новые типы данных для работы с неопределенными значениями.
        2. std::string_view - новый тип для представления строк без копирования.
        3. std::filesystem - новая библиотека для работы с файловой системой.
        4. if constexpr - новая конструкция для условной компиляции на этапе компиляции.
        5. Parallel STL - расширение стандартной библиотеки для параллельного выполнения алгоритмов.
      • Некоторые из ключевых функций C++20:

        1. Концепции (Concepts) - новый механизм для определения требований к шаблонам.
        2. Модули (Modules) - новый механизм для организации кода и ускорения компиляции.
        3. Корутины (Coroutines) - новый механизм для асинхронного программирования.
        4. <ranges> - новая библиотека для работы с диапазонами данных.
        5. <format> - новая библиотека для форматирования строк.
      • Некоторые из ожидаемых функций C++23:

        1. Библиотечная поддержка корутин.
        2. Модульная стандартная библиотека.
        3. Исполнители (Executors) - новый механизм для управления асинхронными задачами.
        4. Сетевое программирование (Networking) - новая библиотека для работы с сетью.
      • Это лишь некоторые из многих новых функций, которые были добавлены в последние версии стандарта C++. Язык C++ продолжает развиваться и предлагать новые возможности для разработчиков. 😊

    4. Расскажите о фичах, которые появились в разных версиях языка.

    • Ответ:
      • Язык C++ продолжает развиваться и вводить новые функции с каждой новой версией стандарта. Вот некоторые из ключевых функций, которые были добавлены в разных версиях языка:

      • C++98:

        1. Шаблоны (Templates) - механизм для обобщенного программирования.
        2. Стандартная библиотека шаблонов (STL) - набор контейнеров, алгоритмов и итераторов.
        3. Исключения (Exceptions) - механизм для обработки ошибок.
        4. Пространства имен (Namespaces) - механизм для организации кода и предотвращения конфликтов имен.
      • C++03:

        1. Улучшения в стандартной библиотеке, такие как std::vector::push_back с перемещением и std::swap с перемещением.
      • C++11:

        1. Автоматическое выведение типа с помощью auto.
        2. Лямбда-выражения (Lambda expressions) - анонимные функции.
        3. Списки инициализации (Initializer lists) - унифицированный синтаксис для инициализации объектов.
        4. Перемещение семантики (Move semantics) - механизм для эффективного перемещения объектов.
        5. nullptr - новый литерал для нулевого указателя.
        6. constexpr - механизм для вычисления выражений на этапе компиляции.
        7. std::thread - новый класс для работы с потоками.
      • C++14:

        1. Обобщенные лямбда-выражения (Generic lambdas) - лямбда-выражения с автоматическим выведением типа параметров.
        2. Переменные шаблона (Variable templates) - шаблоны для переменных.
        3. Улучшенная поддержка constexpr.
        4. Дедукция типа возвращаемого значения функций.
      • C++17:

        1. Структурированные привязки (Structured bindings) - распаковка кортежей и структур в отдельные переменные.
        2. if и switch с инициализацией - новый синтаксис для объявления переменных внутри if и switch.
        3. std::optional, std::variant, std::any - новые типы данных для работы с неопределенными значениями.
        4. std::filesystem - новая библиотека для работы с файловой системой.
      • C++20:

        1. Концепции (Concepts) - механизм для определения требований к шаблонам.
        2. Модули (Modules) - механизм для организации кода и ускорения компиляции.
        3. Корутины (Coroutines) - механизм для асинхронного программирования.
        4. <ranges> - новая библиотека для работы с диапазонами данных.
        5. <format> - новая библиотека для форматирования строк.

    5. Расскажите о модели памяти, которая появилась в С++11 стандарте.

    • Ответ:
      • C++11 ввел новую модель памяти, которая определяет семантику хранения данных в памяти компьютера для абстрактной машины C++. Эта модель памяти обеспечивает портируемость и предсказуемость поведения многопоточных программ на разных платформах и архитектурах.
      • Одной из ключевых особенностей модели памяти C++11 является введение атомарных операций. Атомарные операции позволяют безопасно обновлять данные из нескольких потоков без необходимости использования блокировок. Стандартная библиотека C++11 включает класс std::atomic, который предоставляет набор атомарных операций для различных типов данных.
      • Кроме того, C++11 ввел новые барьеры памяти, которые позволяют контролировать порядок выполнения операций с памятью в многопоточных программах. Барьеры памяти используются для синхронизации доступа к данным между несколькими потоками и предотвращения состояний гонки.
      • В целом, модель памяти C++11 обеспечивает более надежную и предсказуемую основу для разработки многопоточных программ на C++.

    6. Что такое сериализация? Какие библиотеки знаете?

    • Ответ:
      • Сериализация - это процесс преобразования объекта или структуры данных в последовательность байтов, которую можно сохранить или передать. Сериализация используется для сохранения состояния объектов в файлы или базы данных, а также для передачи объектов между разными системами через сеть.

      • В C++ нет встроенной поддержки сериализации, но существует множество библиотек, которые предоставляют эту функциональность. Некоторые из популярных библиотек для сериализации в C++:

        1. Boost.Serialization - часть библиотеки Boost, предоставляет мощные средства для сериализации и десериализации объектов.
        2. cereal - легковесная и быстрая библиотека для сериализации в C++11.
        3. Google Protocol Buffers - библиотека от Google для сериализации структурированных данных.
        4. MessagePack - эффективная бинарная библиотека для сериализации.
      • Эти библиотеки предлагают различные форматы сериализации, такие как XML, JSON и бинарные форматы, и имеют различные возможности и характеристики производительности. Выбор библиотеки зависит от требований к проекту и личных предпочтений разработчика.

    7. Какие знаете паттерны проектирования?

    • Ответ:
      • Паттерны проектирования - это общие, повторно используемые решения для часто встречающихся проблем в контексте разработки программного обеспечения. Они представляют собой формализованные лучшие практики, которые программист может использовать для решения общих проблем при проектировании приложения или системы.

      • Существует множество паттернов проектирования, которые можно классифицировать по различным критериям. Одна из распространенных классификаций - это разделение паттернов на три категории: порождающие, структурные и поведенческие. Порождающие паттерны отвечают за удобное и безопасное создание объектов. Некоторые известные порождающие паттерны: Singleton, Factory Method, Abstract Factory, Builder, Prototype.

      • Структурные паттерны занимаются композицией классов и объектов. Они помогают создавать большие структуры из маленьких компонентов. Некоторые известные структурные паттерны: Adapter, Bridge, Composite, Decorator, Facade.

      • Поведенческие паттерны определяют взаимодействие между объектами и распределение обязанностей. Они помогают управлять потоком данных и выполнением операций. Некоторые известные поведенческие паттерны: Chain of Responsibility, Command, Interpreter, Iterator, Mediator.

    8. Что такое операционная система? Какие существуют типы по назначению?

    • Ответ:
      • Операционная система (ОС) - это системное программное обеспечение, которое управляет компьютерным оборудованием и программными ресурсами, а также предоставляет общие услуги для компьютерных программ¹. Операционные системы используются на многих устройствах, содержащих компьютер - от мобильных телефонов и игровых приставок до веб-серверов и суперкомпьютеров¹.
      • Операционные системы могут быть классифицированы по различным критериям, в том числе по назначению. Некоторые из типов операционных систем по назначению:
        1. Операционные системы для настольных компьютеров: такие как Microsoft Windows, macOS и Linux, предназначены для использования на настольных компьютерах и ноутбуках.
        2. Операционные системы для мобильных устройств: такие как Android и iOS, предназначены для использования на смартфонах и планшетах.
        3. Операционные системы для серверов: такие как Windows Server и Linux, предназначены для использования на серверах и обеспечения высокой надежности и производительности.
        4. Встроенные операционные системы: такие как FreeRTOS и VxWorks, предназначены для использования во встроенных системах с ограниченными ресурсами.
        5. Реального времени операционные системы: такие как QNX и RTLinux, предназначены для использования в системах реального времени, где требуется быстрый отклик и точное соблюдение временных ограничений.

    9. Назвать основные составляющие и принципы работы ОС Linux в качестве примера системы общего назначения.

    • Ответ:
      • Linux - это операционная система общего назначения, которая используется на множестве устройств, от настольных компьютеров и серверов до мобильных телефонов и встроенных систем². Архитектура Linux включает в себя несколько основных компонентов¹:

        1. Ядро (Kernel): ядро является основным компонентом операционной системы Linux. Оно управляет аппаратными ресурсами компьютера, такими как процессор, память и устройства ввода-вывода, и предоставляет службы для запуска программ.
        2. Системные библиотеки (System Libraries): системные библиотеки содержат функции, которые используются для реализации функциональности операционной системы. Они предоставляют интерфейс для доступа к службам ядра и другим системным ресурсам.
        3. Оболочка (Shell): оболочка является интерфейсом пользователя к ядру. Она предоставляет командную строку для ввода команд и скрывает сложность функций ядра от пользователя.
        4. Утилиты (Utilities): утилиты - это набор программ, которые используются для выполнения различных задач, таких как управление файлами, редактирование текста и управление процессами.
      • Принцип работы операционной системы Linux заключается в том, что ядро управляет аппаратными ресурсами и предоставляет службы для запуска программ. Программы используют системные библиотеки для доступа к этим службам и взаимодействия с операционной системой. Пользователь может взаимодействовать с операционной системой через оболочку, вводя команды в командной строке или используя графический интерфейс пользователя. Утилиты предоставляют дополнительные инструменты для выполнения различных задач. 😊

    10 Что такое SFINAE и PIMPL?

    • Ответ:
      • SFINAE (Substitution Failure Is Not An Error) - это принцип в C++, согласно которому недопустимая подстановка параметров шаблона сама по себе не является ошибкой². Это означает, что если во время подстановки аргументов шаблона происходит ошибка, то компилятор просто исключает этот кандидат из списка перегрузок, вместо того, чтобы останавливаться с ошибкой компиляции. Это поведение может быть использовано для реализации условной компиляции и метапрограммирования на этапе компиляции.
      • PIMPL (Pointer to IMPLementation) - это техника программирования на C++, которая удаляет детали реализации класса из его объектного представления, помещая их в отдельный класс, доступ к которому осуществляется через непрозрачный указатель⁴. Эта техника используется для создания интерфейсов библиотек C++ с устойчивым ABI и для уменьшения зависимостей времени компиляции. 😊

    11. Назовите порождающие, структурные и поведенческие паттерны программирования и приведите примеры их использования.

    • Ответ:
      • Паттерны проектирования - это общие, повторно используемые решения для часто встречающихся проблем в контексте разработки программного обеспечения. Они представляют собой формализованные лучшие практики, которые программист может использовать для решения общих проблем при проектировании приложения или системы.

      • Существует множество паттернов проектирования, которые можно классифицировать по различным критериям. Одна из распространенных классификаций - это разделение паттернов на три категории: порождающие, структурные и поведенческие.

      • Порождающие паттерны отвечают за удобное и безопасное создание объектов. Некоторые известные порождающие паттерны:

        1. Singleton: гарантирует, что класс имеет только один экземпляр и предоставляет глобальную точку доступа к этому экземпляру.
        2. Factory Method: определяет интерфейс для создания объектов, но позволяет подклассам решать, какой класс инстанцировать.
        3. Abstract Factory: предоставляет интерфейс для создания семейств взаимосвязанных объектов без указания их конкретных классов.
        4. Builder: отделяет конструирование сложного объекта от его представления, так что один и тот же процесс конструирования может создавать разные представления.
        5. Prototype: указывает виды создаваемых объектов с помощью экземпляра-прототипа и создает новые объекты путем копирования этого прототипа.
      • Структурные паттерны занимаются композицией классов и объектов. Они помогают создавать большие структуры из маленьких компонентов. Некоторые известные структурные паттерны:

        1. Adapter: преобразует интерфейс одного класса в интерфейс другого, который ожидают клиенты.
        2. Bridge: отделяет абстракцию от ее реализации, чтобы они могли изменяться независимо.
        3. Composite: компонует объекты в древовидные структуры для представления иерархий часть-целое.
        4. Decorator: динамически добавляет объектам новые обязанности.
        5. Facade: предоставляет унифицированный интерфейс к группе интерфейсов в подсистеме.
      • Поведенческие паттерны определяют взаимодействие между объектами и распределение обязанностей. Они помогают управлять потоком данных и выполнением операций. Некоторые известные поведенческие паттерны:

        1. Chain of Responsibility: избегает жесткой привязки отправителя запроса к его получателю, давая нескольким объектам возможность обработать запрос.
        2. Command: инкапсулирует запрос в виде объекта, что позволяет параметризовать клиентов с другими запросами, ставить запросы в очередь или регистрировать их, а также поддерживать отмену операций.
        3. Interpreter: определяет представление грамматики и интерпретатор для заданного языка.
        4. Iterator: предоставляет способ последовательного доступа ко всем элементам составного объекта без раскрытия его внутреннего представления.
        5. Mediator: определяет объект, который инкапсулирует способ взаимодействия множества объектов.
  • Препроцессор и компиляция

    12. Расскажите о системах автоматизации билд-процесса.

    • Ответ:
      • Системы автоматизации билд-процесса - это инструменты, которые автоматизируют процесс создания программного обеспечения, включая компиляцию исходного кода в бинарный код, упаковку бинарного кода и запуск автоматических тестов². Эти системы позволяют ускорить и упростить процесс сборки программного обеспечения, а также повысить его надежность и воспроизводимость.
      • Существует множество систем автоматизации билд-процесса, которые отличаются по функциональности, поддерживаемым языкам программирования и платформам. Некоторые из популярных систем автоматизации билд-процесса:
        1. Make: классический инструмент для автоматизации сборки программного обеспечения, использующий Makefile для описания зависимостей между файлами и правил сборки.
        2. CMake: кроссплатформенная система для автоматизации сборки программного обеспечения, которая генерирует файлы сборки для различных систем сборки, таких как Make, Ninja и Visual Studio.
        3. MSBuild: система сборки от Microsoft, используемая в Visual Studio для сборки проектов на языках C++, C# и других.
        4. Ant: система сборки для Java, использующая XML-файлы для описания зависимостей и правил сборки.
        5. Maven: система сборки и управления зависимостями для Java, которая автоматически загружает необходимые библиотеки и плагины.
        6. Gradle: гибкая система сборки для Java, Groovy и Kotlin, которая поддерживает скрипты на Groovy и Kotlin для описания процесса сборки.

    13. Какая разница между статической и динамической библиотеками?

    • Ответ:
      • Статическая библиотека - это набор объектных файлов, которые компилятор или компоновщик могут использовать для создания исполняемого файла. Когда вы компилируете программу, которая использует статическую библиотеку, код из библиотеки копируется в исполняемый файл. Это означает, что каждый исполняемый файл содержит свою собственную копию кода из статической библиотеки.

      • Динамическая библиотека - это набор функций и данных, которые могут быть загружены и использованы несколькими программами одновременно. Когда вы компилируете программу, которая использует динамическую библиотеку, исполняемый файл содержит только ссылки на функции и данные в библиотеке. Код из динамической библиотеки загружается в память только один раз и может быть использован несколькими программами.

      • Основное отличие между статическими и динамическими библиотеками заключается в том, как они используются и распространяются. Статические библиотеки встраиваются непосредственно в исполняемый файл, что увеличивает его размер, но делает его самодостаточным. Динамические библиотеки распространяются отдельно от исполняемого файла и могут быть использованы несколькими программами, что уменьшает размер каждого исполняемого файла и обеспечивает лучшее использование памяти. 😊

    14. Какая разница между исполнительным файлом и динамической библиотекой?

    • Ответ:
      • Исполнительный файл и динамическая библиотека - это два типа файлов, которые используются в компьютерных системах. Они имеют разные цели и используются по-разному.

      • Исполнительный файл - это файл, который содержит машинный код, который может быть непосредственно выполнен процессором компьютера. Исполнительные файлы создаются компилятором из исходного кода программы и содержат все необходимые инструкции для запуска программы. Когда вы запускаете исполнительный файл, операционная система загружает его в память и передает управление процессору, который начинает выполнять инструкции из файла.

      • Динамическая библиотека - это файл, который содержит набор функций и данных, которые могут быть использованы другими программами. Динамические библиотеки используются для обеспечения модульности и повторного использования кода. Когда программа использует функцию из динамической библиотеки, операционная система загружает библиотеку в память и связывает вызовы функций в программе с соответствующими функциями в библиотеке.

      • Основное отличие между исполнительным файлом и динамической библиотекой заключается в том, что исполнительный файл предназначен для непосредственного выполнения, тогда как динамическая библиотека предназначена для использования другими программами. Исполнительные файлы содержат полный код программы и могут быть запущены самостоятельно, тогда как динамические библиотеки содержат только отдельные функции и данные и должны быть использованы вместе с другими программами.

    15. Что такое DLL hell?

    • Ответ:
      • DLL Hell - это термин, используемый для описания проблем, возникающих при работе с динамическими библиотеками (DLL) в операционных системах Microsoft Windows, особенно в устаревших 16-битных версиях, которые работают в едином адресном пространстве¹. DLL Hell может проявляться по-разному, например, приложения могут не запускаться или работать неправильно.
      • Одной из основных причин возникновения DLL Hell является несовместимость версий библиотек. Когда приложение использует динамическую библиотеку, оно ожидает определенную версию этой библиотеки. Однако, если на компьютере установлена другая версия библиотеки, это может привести к ошибкам и сбоям в работе приложения. Это происходит потому, что динамические библиотеки не имеют встроенного механизма обратной совместимости и даже незначительные изменения в библиотеке могут сделать ее несовместимой с предыдущими версиями.
      • Другой причиной возникновения DLL Hell является конфликт между различными версиями одной и той же библиотеки. Например, два разных приложения могут использовать одну и ту же библиотеку, но требовать разные версии этой библиотеки. Это может привести к тому, что одно из приложений будет работать неправильно или вообще не запускаться.
      • Одним из способов решения проблемы DLL Hell является использование механизма сборок (Assemblies) в .NET Framework. Сборки позволяют управлять версиями библиотек и обеспечивают обратную совместимость между различными версиями. Также можно использовать механизм изоляции приложений (Application Isolation), который позволяет каждому приложению использовать свою собственную версию библиотеки, изолированную от других приложений.

    16. Что такое флажки компиляции (fPIC)?

    • Ответ:
      • Флажок компиляции -fPIC (Position Independent Code) используется для генерации кода, который может выполняться независимо от своего абсолютного адреса в памяти. Это означает, что код может быть загружен в любое место в памяти и будет работать корректно, без необходимости перекомпоновки.

      • Флажок -fPIC часто используется при создании динамических библиотек, так как код в динамических библиотеках должен быть независимым от позиции. Это позволяет операционной системе загружать библиотеку в любое свободное место в памяти и использовать ее сразу, без дополнительных затрат на перекомпоновку.

      • Однако использование флажка -fPIC может иметь некоторые недостатки. Например, код, сгенерированный с флажком -fPIC, может быть менее эффективным, чем код, сгенерированный без этого флажка, из-за дополнительных накладных расходов на обращение к данным. Также использование флажка -fPIC может увеличить размер исполняемого файла. 😊

    17. В чем разница между дебаженной и релизной сборкой?

    • Ответ:
      • Дебажная и релизная сборки - это два типа сборок программного обеспечения, которые отличаются по целям и настройкам компиляции.
      • Дебажная сборка предназначена для отладки программы и содержит дополнительную информацию об отладке, такую как символы и номера строк. Эта информация позволяет программистам находить и исправлять ошибки в коде. Дебажные сборки обычно компилируются с флагами, которые упрощают отладку, например, -g в GCC и Clang. Однако из-за дополнительной информации об отладке и меньшей оптимизации дебажные сборки могут работать медленнее, чем релизные.
      • Релизная сборка предназначена для конечных пользователей и содержит оптимизированный код, который работает быстрее, чем код в дебажной сборке. Релизные сборки обычно компилируются с флагами оптимизации, например -O2 или -O3 в GCC и Clang. Однако из-за оптимизации код может быть сложнее отлаживать, поэтому релизные сборки обычно не используются для отладки.
      • В целом, разница между дебажной и релизной сборками заключается в том, что дебажная сборка предназначена для отладки и содержит дополнительную информацию об отладке, тогда как релизная сборка предназначена для конечных пользователей и содержит оптимизированный код. 😊

    18. Что нужно для использования сторонней библиотеки?

    • Ответ:
      • Для использования сторонней библиотеки в своем проекте необходимо выполнить несколько шагов. Вот основные шаги, которые нужно предпринять для использования сторонней библиотеки:
        1. Получить библиотеку: Сначала необходимо получить копию библиотеки, которую вы хотите использовать. Это может быть сделано путем загрузки исходного кода или скомпилированных файлов библиотеки с официального сайта или репозитория.
        2. Установить библиотеку: Если вы загрузили исходный код библиотеки, вам может потребоваться скомпилировать его, чтобы создать скомпилированные файлы библиотеки. Если вы загрузили скомпилированные файлы, вам может потребоваться установить их в определенное место на вашем компьютере.
        3. Настроить проект: Чтобы использовать библиотеку в своем проекте, вам необходимо настроить свой проект, чтобы он знал, где находятся файлы библиотеки. Это может включать в себя добавление путей к файлам заголовков и библиотек в настройки компилятора и компоновщика.
        4. Использовать библиотеку: После того, как вы установили и настроили библиотеку, вы можете начать использовать ее функциональность в своем коде. Это обычно включает в себя добавление директив #include для файлов заголовков библиотеки и вызов функций из библиотеки.

    19. Что такое internal linkage?

    • Ответ:
      • Internal linkage - это свойство идентификатора, которое определяет, может ли он быть доступен только в пределах одного единственного модуля трансляции или нет. Идентификатор с internal linkage может быть виден и использован только в пределах одного модуля трансляции, но он не доступен из других модулей трансляции (то есть он не доступен для линковщика). Это означает, что если два исходных файла имеют одинаково названные идентификаторы с internal linkage, эти идентификаторы будут рассматриваться как независимые (и не приведут к нарушению ODR из-за дублирования определений).
      • Например, чтобы сделать глобальную переменную internal, мы используем ключевое слово static¹. Const и constexpr глобальные переменные имеют internal linkage по умолчанию (и поэтому не нуждаются в ключевом слове static - если оно используется, оно будет проигнорировано). Функции также могут иметь internal linkage с помощью ключевого слова static¹.
      • В современном C++ использование ключевого слова static для предоставления идентификаторам internal linkage выходит из моды. Безымянные пространства имен могут предоставить internal linkage более широкому диапазону идентификаторов (например, типовым идентификаторам), и они лучше подходят для предоставления многим идентификаторам internal linkage.

  • C

    20. Что будет, если дважды вызвать free?

    • Ответ:
      • Вызов функции free дважды на одном и том же указателе может привести к неопределенному поведению¹. Когда вы вызываете free на указателе, вы сообщаете компьютеру, что больше не нуждаетесь в этой области памяти, и он помечает ее как доступную для других данных. Указатель все еще указывает на этот адрес памяти. В этот момент то же самое место в куче может быть возвращено другим вызовом malloc. Когда вы вызываете free второй раз, вы не освобождаете предыдущие данные, а новые данные, и это может быть нежелательно для вашей программы.
      • Таким образом, если вы вызываете free дважды на одном и том же указателе, это может привести к повреждению памяти или худшему. Хорошей практикой является установка освобожденного указателя в NULL, чтобы помочь отладке¹.

    21. Как происходит вызов функции?

    • Ответ:
      • Вызов функции в C++ происходит путем указания имени функции, за которым следуют аргументы, заключенные в круглые скобки. Например, если у нас есть функция с прототипом float area(float, float);, то вызов этой функции может выглядеть так: area(3.0, 4.0);³.
      • Когда программа вызывает функцию, управление передается вызываемой функции⁵. Выполнение кода начинается с первой строки тела функции и продолжается до конца тела функции или до оператора return. После завершения выполнения функции управление возвращается обратно в точку вызова.
      • Важно отметить, что аргументы функции передаются по значению, что означает, что копии значений аргументов передаются в функцию. Это означает, что изменения, внесенные в аргументы внутри функции, не затрагивают исходные значения аргументов в вызывающей программе. Однако если аргумент является указателем или ссылкой на объект, то изменения, внесенные в объект через этот указатель или ссылку, будут отражены в вызывающей программе.

    22. Как происходит передача параметров в функцию?

    • Ответ:
      • В C++ параметры могут передаваться в функцию тремя способами: по значению, по ссылке и по указателю¹.

        1. Передача по значению: когда аргументы передаются в функцию по значению, создается копия значения аргумента, и эта копия передается в функцию. Это означает, что изменения, внесенные в аргументы внутри функции, не затрагивают исходные значения аргументов в вызывающей программе¹.
        2. Передача по ссылке: когда аргументы передаются в функцию по ссылке, передается ссылка на исходный объект, а не его копия. Это означает, что изменения, внесенные в аргументы внутри функции, будут отражены в вызывающей программе¹.
        3. Передача по указателю: когда аргументы передаются в функцию по указателю, передается указатель на исходный объект. Это означает, что изменения, внесенные в объект через этот указатель, будут отражены в вызывающей программе¹.
      • Выбор способа передачи параметров зависит от того, хотите ли вы изменять исходные значения аргументов или нет. Если вы хотите изменять исходные значения аргументов, используйте передачу по ссылке или по указателю. Если вы не хотите изменять исходные значения аргументов, используйте передачу по значению¹.

    23. Как прорабатывается константа переменных?

    • Ответ:
      • Константная переменная в C++ - это переменная, значение которой не может быть изменено после инициализации¹. Для создания константной переменной используется ключевое слово const, которое указывается перед типом данных переменной¹. Например, чтобы создать константную переменную типа int, можно использовать следующий код: const int myNum = 15;¹.
      • Константные переменные используются, когда вы хотите защитить значение переменной от изменений¹. Это может быть полезно, если значение переменной не должно меняться в течение всей программы или если вы хотите предотвратить случайные изменения значения переменной¹.
      • Важно отметить, что константная переменная должна быть инициализирована при объявлении⁴. Попытка изменить значение константной переменной приведет к ошибке компиляции¹.

    24. Что означает ключевое слово inline?

    • Ответ:
      • Ключевое слово inline в C++ используется для объявления функции как встроенной¹. Встроенная функция - это функция, код которой компилятор может вставить непосредственно в точку вызова, вместо того, чтобы генерировать инструкцию вызова функции². Это может ускорить выполнение программы, так как устраняет накладные расходы, связанные с вызовом функции.
      • Однако использование ключевого слова inline не гарантирует, что функция действительно будет встроена. Компилятор самостоятельно принимает решение о том, будет ли функция встроена или нет, на основе различных факторов, таких как размер функции и частота ее вызова.
      • Функция, определенная полностью внутри определения класса/структуры/объединения, является неявно встроенной функцией¹. Функция, объявленная как constexpr, также является неявно встроенной функцией.
      • Следует отметить, что использование встроенных функций может увеличить размер исполняемого файла из-за дублирования кода функции в каждой точке вызова. Поэтому необходимо использовать ключевое слово inline с осторожностью и только тогда, когда это действительно может принести выгоду.

    25. Для чего используют выравнивания, можно ли его контролировать?

    • Ответ:
      • Выравнивание в C++ используется для определения ограничений на адрес памяти, в котором могут быть созданы объекты определенного типа. Адрес памяти считается допустимым для создания объекта, если деление этого адреса на выравнивание объекта дает целое число.
      • Вы можете контролировать выравнивание в C++ с помощью спецификатора alignas, который был добавлен в стандарт C++11. Спецификатор alignas может применяться к объявлению или определению класса, объявлению не-битового поля данных класса или объявлению переменной³. Объект или тип, объявленный с помощью такого объявления, будет иметь требование к выравниванию, равное строгому (наибольшему) ненулевому выражению всех спецификаторов alignas, используемых в объявлении, если это не ослабит естественное выравнивание типа.
      • Например, чтобы указать, что переменная должна быть выровнена по 16-байтной границе, можно использовать следующий код: alignas(16) int x;.
      • Также можно использовать спецификатор alignof для получения требования к выравниванию типа. Например, чтобы узнать требование к выравниванию типа int, можно использовать следующий код: std::size_t alignment = alignof(int);.

    26. Расскажите о битовых полях.

    • Ответ:
      • Битовые поля в C++ - это способ определения членов класса с явным размером в битах¹. Смежные битовые поля могут (или не могут) быть упакованы, чтобы разделять и пересекать отдельные байты.

      • Объявление битового поля является объявлением члена данных класса, которое использует следующий декларатор: identifier(optional) attr(optional) : size. Тип битового поля определяется с помощью decl-specifier-seq синтаксиса объявления. Тип битового поля может быть только целочисленным или ( возможно, квалифицированным cv) типом перечисления.

      • Битовые поля используются, когда вы хотите экономить память, определяя члены класса, которые занимают меньше места, чем целочисленный тип². Например, если у вас есть структура, которая содержит день недели, день месяца и месяц, вы можете использовать битовые поля для экономии памяти, как показано ниже:

      •  struct Date {
             unsigned short nWeekDay : 3; // 0..7 (3 bits)
             unsigned short nMonthDay : 6; // 0..31 (6 bits)
             unsigned short nMonth : 5; // 0..12 (5 bits)
          };
      • В этом примере каждый член структуры занимает только несколько битов, а не целый unsigned short. Однако следует отметить, что использование битовых полей может усложнить код и затруднить отладку. Также есть ограничения на то, что можно делать с битовыми полями. Например, вы не можете получить адрес битового поля или инициализировать не-const ссылку с помощью битового поля.

    27. Для чего нужен extern "C"?

    • Ответ:
      • Ключевое слово extern "C" в C++ используется для указания компилятору, что функция или переменная имеет связь с языком C¹. Это означает, что имя функции или переменной не будет изменено (не будет подвергнуто name mangling) компилятором C++, и оно будет доступно для использования в коде на языке C.
      • Ключевое слово extern "C" часто используется при написании кода, который должен быть совместимым с C и C++. Например, если вы хотите написать библиотеку на C++, которую можно использовать в коде на языке C, вы можете объявить функции библиотеки с помощью extern "C", чтобы они были доступны для вызова из кода на языке C.
      • Важно отметить, что extern "C" может использоваться только на уровне пространства имен и не может использоваться внутри классов. Также следует отметить, что extern "C" игнорируется для членов класса.

    28. Что будет, если в двух файлах сделать функцию с одинаковым именем и параметрами? На каком этапе возникнет ошибка?

    • Ответ:
      • Если вы определите функцию с одинаковым именем и параметрами в двух разных файлах, это нарушит правило одного определения (ODR) в C++¹ . Это правило гласит, что в пределах одной программы не может быть более одного определения любой переменной, функции или класса.

      • Ошибка обычно возникает на этапе компоновки, когда компоновщик обнаруживает, что два объектных файла содержат определения для одной и той же функции. Компоновщик выдаст ошибку множественного определения и откажется создавать исполняемый файл.

      • Один из способов решения этой проблемы - использование пространств имен. Вы можете поместить каждую функцию в свое собственное пространство имен, чтобы избежать конфликта имен. Например:

      •  // fileA.cpp
         namespace FileA {
             void foo(int a) {
                 // Some code for file A
             }
         }
         
         // fileB.cpp
         namespace FileB {
             void foo(int a) {
                 // Some code for file B
             }
         }
      • Теперь вы можете использовать обе функции в своей программе, указывая пространство имен перед именем функции: FileA::foo(1); и FileB::foo(2);.

    29. Как экспортировать/импортировать функции из динамической библиотеки?

    • Ответ:
      • Для экспорта функций из динамической библиотеки в C++ вы можете использовать спецификатор __declspec(dllexport) (для Windows) или атрибут __attribute__((visibility("default"))) (для Linux) перед объявлением функции¹. Это указывает компилятору, что функция должна быть экспортирована из динамической библиотеки и доступна для использования в других программах¹.

      • Например, чтобы экспортировать функцию foo из динамической библиотеки в Windows, вы можете использовать следующий код:

      •  extern "C" __declspec(dllexport) void foo() {
             // Код функции
         }
      • Для импорта функций из динамической библиотеки в C++ вы можете использовать спецификатор __declspec(dllimport) (для Windows) перед объявлением функции². Это указывает компилятору, что функция находится в динамической библиотеке и должна быть импортирована².

      • Например, чтобы импортировать функцию foo из динамической библиотеки в Windows, вы можете использовать следующий код:

      •  extern "C" __declspec(dllimport) void foo();
      • Затем вы можете вызывать функцию foo в своей программе, как обычно. Компоновщик найдет динамическую библиотеку и свяжет вызовы функций с экспортированными функциями в динамической библиотеке².

    30. Какая разница между С-style приведением типов и C++ приведением?

    • Ответ:
      • C-style приведение типов и C++ приведение типов имеют несколько отличий. Одно из главных отличий заключается в том, что C++ приведения типов проверяются компилятором, в то время как C-style приведения типов не проверяются и могут привести к ошибкам во время выполнения¹ . Кроме того, C++ приведения типов легче найти в коде, в то время как поиск C-style приведений типов может быть затруднительным¹ .
      • Еще одно преимущество C++ приведений типов заключается в том, что они более ясно выражают намерения программиста. В C++ есть 4 разных приведения типов: static_cast, dynamic_cast, const_cast и reinterpret_cast, каждое из которых имеет свое специфическое назначение.
      • Например, static_cast используется для преобразования между совместимыми типами и является более строгим, чем C-style приведение типов. Он позволяет компилятору проверить совместимость типов указателя и указываемого объекта, что позволяет программисту обнаружить неправильное присваивание указателей во время компиляции.
      • В целом, использование C++ приведений типов считается более безопасным и предпочтительным, чем использование C-style приведений типов в коде на C++.

  • C++

    31. Что такое явное и неявное приведение типов в С++? Зачем делать explicit-конструктор?

    • Ответ:
      • Явное приведение типов в C++ происходит, когда программист явно указывает, что значение одного типа должно быть преобразовано в значение другого типа. Это делается с помощью операторов приведения типов, таких как static_cast, dynamic_cast, const_cast и reinterpret_cast¹.

      • Неявное приведение типов в C++ происходит автоматически, когда компилятор обнаруживает, что значение одного типа используется в контексте, где ожидается значение другого типа. Например, если вы пытаетесь присвоить значение типа int переменной типа double, компилятор автоматически преобразует значение int в значение double⁴.

      • Ключевое слово explicit используется для предотвращения неявных преобразований¹. Оно может быть использовано с конструкторами и функциями преобразования для указания того, что они не могут использоваться для неявных преобразований и копирования инициализации.

      • Например, если у вас есть класс с конструктором, который принимает один аргумент, вы можете использовать ключевое слово explicit, чтобы предотвратить неявное преобразование аргумента в объект вашего класса. Это может быть полезно, если вы хотите избежать ошибок, связанных с неожиданными неявными преобразованиями.

      • Вот простой пример использования ключевого слова explicit с конструктором:

      •   class MyClass {
          public:
              explicit MyClass(int x) : m_x(x) {}
          private:
              int m_x;
          };
          
          int main() {
              MyClass a = 5; // Ошибка: неявное преобразование запрещено
              MyClass b(5); // ОК: явное преобразование
          }
      • В этом примере конструктор класса MyClass объявлен как explicit, поэтому он не может использоваться для неявного преобразования значения int в объект класса MyClass. Попытка сделать это вызывает ошибку компиляции. Однако вы все еще можете использовать конструктор для явного преобразования значения int в объект класса MyClass, как показано в строке с объявлением переменной b.

    32. Что такое Uniform initialization? Aggregate initialization?

    • Ответ:
      • Uniform initialization, введенная в C++11, позволяет использовать единый синтаксис для инициализации переменных и объектов, начиная от примитивных типов до агрегатов¹. Другими словами, она вводит инициализацию скобками, которая использует фигурные скобки {} для заключения значений инициализатора. Синтаксис выглядит следующим образом: type var_name {arg1, arg2, ....arg n}.

      • Aggregate initialization - это форма инициализации списком для массивов или типов класса (часто структур или объединений), которые имеют: нет приватных или защищенных членов, нет пользовательских конструкторов, кроме явно заданных или удаленных конструкторов . Это позволяет напрямую инициализировать элементы агрегатов. Для этого мы предоставляем список инициализаторов в качестве инициализатора, который представляет собой список значений, разделенных запятыми.

      • Например, если у вас есть структура с несколькими членами:

      • struct Employee {
            int id {};
            int age {};
            double wage {};
        };
      • Когда мы определяем объект с типом структуры, нам нужен способ инициализации нескольких членов во время инициализации:

      •  Employee joe; // как мы инициализируем joe.id, joe.age и joe.wage?
      • Агрегаты используют форму инициализации, называемую агрегатной инициализацией, которая позволяет нам напрямую инициализировать элементы агрегатов. Для этого мы предоставляем список инициализаторов в качестве инициализатора, который представляет собой список значений, разделенных запятыми. Так же как обычные переменные могут быть скопированы, напрямую инициализированы или списком инициализированы, есть 3 формы агрегатной инициализации.