ch
Feedback

不要被骗子欺骗!Telemetrio 会找到并标记这些频道 👉 如果想查看标记,请订阅

C/C++ | Вопросы собесов

C/C++ | Вопросы собесов

前往频道在 Telegram

Сайт: https://easyoffer.ru/ Все каналы: t.me/+xGeAw6ckJ4liYzQy Контакт для рекламы: @sendme_ads

显示更多
4 191
订阅者
-124 小时
-67 天
-730 天
帖子存档
Яндекс Музыка до 360 дней бесплатно Яндекс Музыка для вас и 3-х ваших близких. Кинопоиск и Яндекс Книги тоже в мультиподписке
Яндекс Музыка до 360 дней бесплатно Яндекс Музыка для вас и 3-х ваших близких. Кинопоиск и Яндекс Книги тоже в мультиподписке Плюс. Попробуйте бесплатно❤️ Слушать #реклама 18+ music.yandex.ru О рекламодателе

🤔 Что будет если для беззнаковой переменной равной 0 сделать декремент? Если вы сделаете декремент (--) для беззнаковой переменной, значение которой равно 0, это приведет к переполнению, и переменная примет максимальное значение, которое может быть представлено этим типом. Рассмотрим, что произойдет с переменной типа unsigned int, если её значение равно 0 и мы применим к ней декремент:
#include <iostream>

int main() {
    unsigned int x = 0;
    std::cout << "x before decrement: " << x << std::endl;
    x--; // Декрементирование беззнаковой переменной 0
    std::cout << "x after decrement: " << x << std::endl; // Переполнение
    return 0;
}
Для беззнаковой переменной unsigned int: Диапазон значений: от 0 до 4294967295 (для 32-битной реализации). Когда переменная x равна 0 и мы выполняем x--, происходит следующее: 🟠Значение 0 уменьшается на 1. 🟠Поскольку unsigned int не может представлять отрицательные значения, происходит переполнение. 🟠Переполнение для беззнакового типа приводит к "оборачиванию" значения через максимальное значение типа. 🟠Максимальное значение для unsigned int (при 32-битной реализации) равно 4294967295. Таким образом, после выполнения x--, значение x становится 4294967295. Результат выполнения приведенного выше кода будет следующим:
x before decrement: 0
x after decrement: 4294967295
Для беззнаковых типов в C++ (например, unsigned char, unsigned short, unsigned int, unsigned long): Если значение переменной равно 0 и к ней применяется декремент, результатом будет максимальное значение этого типа. Ставь 👍 и забирай 📚 Базу знаний

Надежные VDS-сервера в NetAngels от 130₽/месяц Подберем мощные VDS-сервер для любых задач. Техподдержка 24/7. Защита от DDoS-
Надежные VDS-сервера в NetAngels от 130₽/месяц Подберем мощные VDS-сервер для любых задач. Техподдержка 24/7. Защита от DDoS-атак. Гибкая конфигурация. Бесплатный перенос VDS с сохранением всех данных. Попробуйте VDS-сервер от NetAngels уже сегодня! Перейти на сайт #реклама 16+ netangels.ru О рекламодателе

🤔 Из чего состоит shared_ptr? shared_ptr состоит из указателя на объект и счетчика ссылок, управляющего временем жизни объекта. Когда количество ссылок становится нулевым, shared_ptr автоматически удаляет объект. Он также поддерживает совместное использование ресурсов между несколькими владельцами, что делает его удобным для совместного владения. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚Базу знаний

🤔 Что такое POD тип ? POD (Plain Old Data) тип — это простой тип данных в C++, который совместим с C и обладает определенными свойствами, делающими его поведение предсказуемым и простым. 🚩Основные характеристики POD типов 🟠Тривиальность (Trivial) Тривиальный конструктор (конструктор по умолчанию, копирования и перемещения). Тривиальный деструктор. Компилятор генерирует их автоматически, если они не определены явно. 🟠Стандартная макетируемость (Standard Layout) Все нестатические члены данных имеют один и тот же доступ (public, private, или protected). Нет виртуальных функций и нет виртуальных базовых классов. Все базовые классы являются POD типами. Стандартные правила выравнивания и макетирования данных в памяти.
struct MyPOD {
    int x;
    float y;
};
Пример НЕ POD типа
struct NonPOD {
    int x;
    virtual void func() {}  // Наличие виртуальной функции делает тип не POD
};
🚩Применение POD типов 🟠Совместимость с C Поскольку POD типы совместимы с C, они могут быть безопасно использованы в смешанных C/C++ программах. 🟠Серилизация Легко сохранять и загружать из файлов или передавать по сети, так как их память может быть прочитана или записана напрямую. 🟠Оптимизация Компиляторы могут лучше оптимизировать код с POD типами из-за их простоты и предсказуемости. 🚩Проверка, является ли тип POD В C++11 и новее можно использовать стандартную библиотеку для проверки, является ли тип POD
#include <type_traits>

struct MyPOD {
    int x;
    float y;
};

struct NonPOD {
    int x;
    virtual void func() {}
};

int main() {
    std::cout << std::is_pod<MyPOD>::value << std::endl;  // вывод: 1 (true)
    std::cout << std::is_pod<NonPOD>::value << std::endl; // вывод: 0 (false)
    return 0;
}
Ставь 👍 и забирай 📚 Базу знаний

Новый ритм на побережье с Jumeirah Al Naseem Новый взгляд на Jumeirah Al Naseem — современная элегантность побережья и гармония душевных моментов на берегу Дубая Забронировать #реклама jumeirah.com О рекламодателе

🤔 В unordered_set поиск — константа? Поиск в unordered_set имеет среднюю сложность O(1), но в худшем случае при хеш-коллизиях может быть O(n). Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚Базу знаний

🤔 Для чего при перегрузке оператора присваивания возвращать ссылку? При перегрузке оператора = в C++ принято возвращать ссылку на текущий объект (*this). Это позволяет цепочечное присваивание (a = b = c), а также улучшает производительность и удобство использования. 🚩Правильная сигнатура оператора `=`
class MyClass {
public:
    MyClass& operator=(const MyClass& other) {
        if (this != &other) {  // Защита от самоприсваивания
            // Копируем данные
        }
        return *this; // Возвращаем ссылку на текущий объект
    }
};
🚩Что даёт возвращение ссылки (`T&`)? Поддержка цепочки присваивания (a = b = c)
a = b = c;  // Интерпретируется как a = (b = c);
Пример
#include <iostream>

class MyClass {
public:
    int value;
    MyClass(int v) : value(v) {}

    MyClass& operator=(const MyClass& other) {
        if (this != &other) { // Защита от самоприсваивания
            value = other.value;
        }
        return *this;
    }
};

int main() {
    MyClass a(1), b(2), c(3);
    a = b = c; // Работает, потому что `b = c` возвращает ссылку на `b`
    std::cout << "a: " << a.value << ", b: " << b.value << ", c: " << c.value << std::endl;
}
Вывод
a: 3, b: 3, c: 3
🟠Убираем лишнее копирование (повышаем производительность) Если оператор = вернёт не ссылку, а объект, то произойдёт лишнее копирование. Плохо (возвращаем объект, а не ссылку)
MyClass operator=(const MyClass& other) { // ⚠️ Ошибка: возвращаем объект
    value = other.value;
    return *this; // Здесь создаётся временный объект (лишняя копия!)
}
Хорошо (возвращаем T&)
MyClass& operator=(const MyClass& other) { // ✅ Возвращаем ссылку
    value = other.value;
    return *this;
}
🚩Поддержка `if (a = b)` Если оператор = вернёт ссылку, мы можем использовать его в условии:
if ((a = b).value == 42) { // ОК
    std::cout << "Присваивание выполнено, a == 42";
}
🚩Что если вернуть `void`? Если оператор = вернёт void, цепочка a = b = c; не будет работать.
class MyClass {
public:
    void operator=(const MyClass& other) { // ⚠️ Ошибка!
        value = other.value;
    }
};

int main() {
    MyClass a, b, c;
    a = b = c; // ❌ Ошибка: `b = c` возвращает void, а `a = void` не работает!
}
Ставь 👍 и забирай 📚 Базу знаний

🤔 Как называется одинаковый результат после применения хэш-функции? Это называется коллизия (hash collision) — ситуация, при которой два разных значения дают один и тот же хэш. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚Базу знаний

🤔 Что такое функции обратного вызова? Это функции, которые передаются как аргументы другим функциям, чтобы быть вызванными в определенный момент. Это важный механизм в программировании, который позволяет реализовывать гибкие и модульные решения, особенно в контексте асинхронного программирования, обработчиков событий и многопоточности. 🚩Основные характеристики 🟠Передача как параметр Функции обратного вызова передаются как параметры другим функциям. 🟠Вызов в определенный момент Они вызываются внутри функции, которая их получила, в определенный момент времени или по определенному условию. 🟠Гибкость и расширяемость Использование функций обратного вызова позволяет легко изменять поведение программы без необходимости изменения основной функции. 🟠Пример на С В языке C функции обратного вызова обычно реализуются с использованием указателей на функции.
#include <stdio.h>

// Объявление функции обратного вызова
void callback_function(int value) {
    printf("Callback called with value: %d\n", value);
}

// Функция, принимающая указатель на функцию в качестве параметра
void process_value(int value, void (*callback)(int)) {
    // Некоторая обработка
    value *= 2;

    // Вызов функции обратного вызова
    callback(value);
}

int main() {
    int x = 5;
    // Передача функции обратного вызова
    process_value(x, callback_function);
    return 0;
}
🟠Пример на C++ В C++ можно использовать указатели на функции, объекты-функции (functors) и лямбда-функции в качестве функций обратного вызова.
#include <iostream>
#include <functional>

// Функция обратного вызова
void callback_function(int value) {
    std::cout << "Callback called with value: " << value << std::endl;
}

// Функция, принимающая std::function в качестве параметра
void process_value(int value, std::function<void(int)> callback) {
    // Некоторая обработка
    value *= 2;

    // Вызов функции обратного вызова
    callback(value);
}

int main() {
    int x = 5;

    // Использование указателя на функцию
    process_value(x, callback_function);

    // Использование лямбда-функции
    process_value(x, [](int val) {
        std::cout << "Lambda callback called with value: " << val << std::endl;
    });

    // Использование объекта-функции
    struct Functor {
        void operator()(int val) {
            std::cout << "Functor callback called with value: " << val << std::endl;
        }
    };

    Functor functor;
    process_value(x, functor);

    return 0;
}
🚩Применение в асинхронном программировании Функции обратного вызова широко используются в асинхронном программировании, где задачи выполняются параллельно или по завершении некоторых событий. Это позволяет программе не блокироваться, ожидая завершения длительных операций. Пример с асинхронным вызовом
#include <iostream>
#include <thread>

// Функция обратного вызова
void callback_function() {
    std::cout << "Async operation completed" << std::endl;
}

// Асинхронная функция
void async_operation(std::function<void()> callback) {
    std::thread([callback]() {
        // Имитация длительной операции
        std::this_thread::sleep_for(std::chrono::seconds(2));

        // Вызов функции обратного вызова
        callback();
    }).detach();
}

int main() {
    std::cout << "Starting async operation..." << std::endl;
    async_operation(callback_function);
    
    // Основной поток продолжает выполнять свою работу
    std::cout << "Main thread continues..." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(3));

    return 0;
}
Ставь 👍 и забирай 📚 Базу знаний

🤔 Как работает счетчик ссылок в shared_ptr? В shared_ptr хранится счётчик ссылок, который увеличивается при создании новой копии и уменьшается при уничтожении указателя. Когда счётчик достигает нуля, объект автоматически освобождается из памяти. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚Базу знаний

🤔 Какие виды конструкторов могут быть у класса? 🟠Конструктор по умолчанию (Default Constructor) Без параметров, создается автоматически компилятором, если не задан.
   class MyClass {
   public:
       MyClass() {}
   };
🟠Параметризованный конструктор (Parameterized Constructor) Принимает параметры для инициализации объекта.
   class MyClass {
   private:
       int x;
   public:
       MyClass(int value) : x(value) {}
   };
🟠Конструктор копирования (Copy Constructor) Создает копию существующего объекта.
   class MyClass {
   private:
       int x;
   public:
       MyClass(const MyClass &other) : x(other.x) {}
   };
🟠Конструктор перемещения (Move Constructor) Перемещает ресурсы из временного объекта.
   class MyClass {
   private:
       int* data;
   public:
       MyClass(MyClass&& other) noexcept : data(other.data) {
           other.data = nullptr;
       }
   };
🟠Делегирующий конструктор (Delegating Constructor) Вызывает другой конструктор того же класса.
   class MyClass {
   private:
       int x, y;
   public:
       MyClass(int value) : MyClass(value, 0) {}
       MyClass(int value1, int value2) : x(value1), y(value2) {}
   };
🟠Явный конструктор (Explicit Constructor) Помечен explicit для предотвращения неявных преобразований.
   class MyClass {
   public:
       explicit MyClass(int value) {}
   };
Ставь 👍 и забирай 📚 Базу знаний

🤔 Что лежит в основе работы unordered_set, unordered_map и set, map? 1. Set и Map: основаны на сбалансированных бинарных деревьях, обычно красно-чёрных. Они поддерживают упорядоченность данных. 2. Unordered_set и Unordered_map: основаны на хеш-таблицах, обеспечивающих быструю вставку, удаление и поиск. 3. Основной принцип различия — структура данных: деревья для упорядоченности, хеш-таблицы для скорости. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚Базу знаний

🤔 Можно использовать в контексте нескольких потоков Shared_ptr и потокобезопасный он или нет? Использование shared_ptr в многопоточном контексте требует определённой осторожности, поскольку он сам по себе имеет ограниченную потокобезопасность. Рассмотрим подробнее, что это значит и как можно безопасно использовать его в многопоточной среде. 🚩Потокобезопасность 🟠Безопасное копирование и удаление Стандарт C++ гарантирует, что операции копирования и удаления shared_ptr являются потокобезопасными в том смысле, что вы можете безопасно копировать и удалять разные экземпляры shared_ptr из разных потоков, даже если они указывают на один и тот же объект. 🟠Счётчик ссылок Модификации счётчика ссылок в нем (т.е. увеличение и уменьшение) являются атомарными операциями, что означает, что при изменении счётчика из разных потоков состояние счётчика останется консистентным и корректным. 🟠Ограничения потокобезопасности Не рекомендуется изменять сам объект, на который указывает shared_ptr, из нескольких потоков без дополнительной синхронизации, так как shared_ptr не предоставляет потокобезопасность для доступа к объекту. Если несколько потоков изменяют один и тот же shared_ptr (не копии), например, присваивают ему новые значения или обнуляют его, это должно синхронизироваться, так как стандартная библиотека не гарантирует безопасность таких операций без внешней синхронизации. 🚩Рекомендации для использования в многопоточной среде 🟠Избегайте разделять один и тот же `shared_ptr` между потоками Лучше дать каждому потоку собственную копию shared_ptr, что позволяет избежать конфликтов на запись. 🟠Синхронизируйте доступ к общему shared_ptr Если несколько потоков должны иметь доступ к одному и тому же shared_ptr, используйте мьютексы или другие механизмы синхронизации для защиты его при записи. 🟠Рассмотрите использование std::atomic<std::shared_ptr<T>> для C++20 и выше Эта оболочка предоставляет потокобезопасные операции на shared_ptr без необходимости явной синхронизации при изменении указателя. Ставь 👍 и забирай 📚 Базу знаний

🤔 Сложность операций с vector и list Для vector добавление элемента в конец имеет сложность O(1) в среднем, но при перераспределении памяти — O(n). Удаление и вставка в произвольное место занимают O(n), так как элементы необходимо сдвигать. Для list добавление и удаление имеют сложность O(1), но доступ по индексу — O(n), так как требуется последовательный обход элементов. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚Базу знаний

🤔 Как работает счетчик у shared_ptr? shared_ptr использует счетчик ссылок для управления памятью, что позволяет нескольким умным указателям делить владение одним объектом. Это механизм, который подсчитывает количество экземпляров shared_ptr, которые указывают на тот же управляемый объект. Когда новый shared_ptr создается и указывает на объект, счетчик ссылок увеличивается. Когда shared_ptr уничтожается или перестает указывать на объект, счетчик уменьшается. Объект, на который указывали, освобождается (то есть его деструктор вызывается), когда счетчик ссылок достигает нуля. Это гарантирует, что память освобождается корректно и в нужный момент. 🚩Как он работает 🟠Инициализация и копирование Когда создается shared_ptr и он начинает управлять объектом, создается "блок управления" (control block), содержащий счетчик ссылок. Каждый раз, когда создается новый shared_ptr, который указывает на тот же объект (через копирование или присваивание), счетчик ссылок увеличивается. 🟠Уничтожение и перемещение Когда shared_ptr уничтожается или его значение присваивается другому shared_ptr, счетчик ссылок уменьшается. Если shared_ptr перемещается (используя move-семантику), счетчик ссылок не изменяется, так как владение объектом передается новому shared_ptr, а старый перестает владеть объектом. 🟠Освобождение ресурсов Как только счетчик ссылок достигает нуля (то есть последний shared_ptr, указывающий на объект, был уничтожен или переназначен), объект удаляется. Соответственно, если для объекта был задан пользовательский делетер, он вызывается вместо стандартного delete. 🚩Счетчик слабых ссылок shared_ptr также поддерживает weak_ptr, который представляет собой "слабую" ссылку на объект. weak_ptr позволяет наблюдать за объектом, не увеличивая счетчик сильных ссылок: Счетчик слабых ссылок отслеживает, сколько weak_ptr указывает на объект. Он не предотвращает удаление объекта, когда счетчик сильных ссылок достигает нуля. Когда счетчик сильных ссылок достигает нуля и объект удаляется, счетчик слабых ссылок все еще отслеживает, сколько weak_ptr наблюдают за объектом. Объект блока управления остается, пока не исчезнут все weak_ptr. Ставь 👍 и забирай 📚 Базу знаний

🤔 Какие есть операторы приведения типов? 1. static_cast: Для явного и безопасного приведения типов, известных на этапе компиляции. 2. dynamic_cast: Для приведения указателей или ссылок в иерархии классов с проверкой типов во время выполнения. 3. const_cast: Для снятия или добавления const и volatile квалификаторов. 4. reinterpret_cast: Для низкоуровневого преобразования указателей или других типов без проверки безопасности. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚Базу знаний

🤔 Какие знаешь сортировки? Существует множество алгоритмов сортировки, каждый из которых имеет свои преимущества и недостатки в зависимости от условий использования. Рассмотрим основные из них. 🟠Сортировка пузырьком (Bubble Sort) Простейший алгоритм, который многократно проходит по массиву, сравнивая соседние элементы и меняя их местами, если они стоят в неправильном порядке. Сложность: O(n²) в худшем и среднем случаях, O(n) в лучшем случае (если массив уже отсортирован). Когда использовать: Почти никогда, так как слишком медленный.
void bubbleSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                std::swap(arr[j], arr[j + 1]);
            }
        }
    }
}
🟠Сортировка выбором (Selection Sort) На каждом шаге ищется минимальный элемент и ставится в начало неотсортированной части массива. Сложность: O(n²) всегда. Когда использовать: Если важна простота реализации, но нужна немного лучшая производительность, чем у пузырьковой сортировки.
void selectionSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        int minIdx = i;
        for (int j = i + 1; j < n; j++) {
            if (arr[j] < arr[minIdx]) {
                minIdx = j;
            }
        }
        std::swap(arr[i], arr[minIdx]);
    }
}
🟠Сортировка вставками (Insertion Sort) Берём один элемент и вставляем его в правильное место среди уже отсортированных элементов. Сложность: O(n²) в худшем случае, O(n) в лучшем (если массив почти отсортирован). Когда использовать: Для небольших массивов или почти отсортированных данных.
void insertionSort(int arr[], int n) {
    for (int i = 1; i < n; i++) {
        int key = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
}
🟠Сортировка слиянием (Merge Sort) Разделяем массив на две части, рекурсивно сортируем их и затем сливаем. Сложность: O(n log n) всегда. Когда использовать: Когда нужна стабильность и предсказуемая скорость работы.
void merge(int arr[], int l, int m, int r) {
    int n1 = m - l + 1, n2 = r - m;
    int L[n1], R[n2];
    for (int i = 0; i < n1; i++) L[i] = arr[l + i];
    for (int i = 0; i < n2; i++) R[i] = arr[m + 1 + i];

    int i = 0, j = 0, k = l;
    while (i < n1 && j < n2) arr[k++] = (L[i] < R[j]) ? L[i++] : R[j++];
    while (i < n1) arr[k++] = L[i++];
    while (j < n2) arr[k++] = R[j++];
}

void mergeSort(int arr[], int l, int r) {
    if (l < r) {
        int m = l + (r - l) / 2;
        mergeSort(arr, l, m);
        mergeSort(arr, m + 1, r);
        merge(arr, l, m, r);
    }
}
Ставь 👍 и забирай 📚 Базу знаний

🔥 Скрытые вакансии с удаленной работой для C/C++ разработчика, которые нигде больше не публикуются. Напрямую от компаний, с
🔥 Скрытые вакансии с удаленной работой для C/C++ разработчика, которые нигде больше не публикуются. Напрямую от компаний, с контактами рекрутеров. 🔹 C/C++ Jobs | Вакансии 🔸 Все каналы с вакансиями

🤔 Как работает быстрая сортировка? Быстрая сортировка (quick sort) — это алгоритм, который рекурсивно делит массив на подмассивы на основе опорного элемента (pivot). Элементы, меньшие опорного, перемещаются в левую часть массива, а элементы, большие — в правую. Процесс продолжается рекурсивно для каждого подмассива, пока все элементы не будут отсортированы. В среднем случае быстрая сортировка имеет сложность O(n log n), но в худшем — O(n^2), если выбирать неудачный опорный элемент. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚Базу знаний