
- Главная
- Каталог
- Интернет технологии
- C/C++ | Вопросы собесов
C/C++ | Вопросы собесов
Разбираем вопросы с собеседований на С/С++ разработчика
Статистика канала
#include <iostream>
class Base {
public:
Base() { std::cout << "Конструктор Base\n"; }
~Base() { std::cout << "Деструктор Base\n"; }
};
class Derived : public Base {
public:
Derived() { std::cout << "Конструктор Derived\n"; }
~Derived() { std::cout << "Деструктор Derived\n"; }
};
int main() {
Derived obj;
}{}
Вывод
Конструктор Base
Конструктор Derived
Деструктор Derived
Деструктор Base{}
🚩Порядок вызова конструкторов для членов класса
Члены класса инициализируются в порядке их объявления в классе (а не в порядке в списке инициализации).
class A {
public:
A() { std::cout << "A()\n"; }
~A() { std::cout << "~A()\n"; }
};
class B {
public:
B() { std::cout << "B()\n"; }
~B() { std::cout << "~B()\n"; }
};
class C {
A a;
B b;
public:
C() { std::cout << "C()\n"; }
~C() { std::cout << "~C()\n"; }
};
int main() {
C obj;
}{}
Вывод
A()
B()
C()
~C()
~B()
~A(){}
Ставь 👍 и забирай 📚 Базу знаний
class DatabaseConnection {
public:
DatabaseConnection(const std::string& connectionString) {
if (!connect(connectionString)) {
throw std::runtime_error("Connection failed");
}
}
private:
bool connect(const std::string& connectionString) {
// Логика подключения
return false; // Имитация ошибки подключения
}
};{}
🟠Безопасность ресурсов
Конструкторы часто выделяют ресурсы, такие как память, файлы или сетевые соединения. Если инициализация не удалась, необходимо убедиться, что все выделенные ресурсы будут корректно освобождены. Исключения помогают автоматизировать этот процесс, используя RAII (Resource Acquisition Is Initialization). Если файл не удалось открыть, исключение будет выброшено, и деструктор корректно освободит ресурсы.
class FileHandler {
public:
FileHandler(const std::string& filename) {
file = fopen(filename.c_str(), "r");
if (!file) {
throw std::runtime_error("Failed to open file");
}
}
~FileHandler() {
if (file) {
fclose(file);
}
}
private:
FILE* file;
};{}
🟠Целостность программы
Исключения в конструкторах позволяют предотвратить использование некорректно инициализированных объектов. Это важно для поддержания целостности программы и избежания непредсказуемых ошибок. Если загрузка конфигурации не удалась, исключение предотвращает создание некорректного объекта ConfigLoader.
class ConfigLoader {
public:
ConfigLoader(const std::string& configPath) {
if (!loadConfig(configPath)) {
throw std::runtime_error("Failed to load config");
}
}
private:
bool loadConfig(const std::string& configPath) {
// Логика загрузки конфигурации
return false; // Имитация ошибки загрузки
}
};{}
🚩Обработка исключений в конструкторах
Исключения в конструкторах должны быть обработаны на более высоком уровне программы, чтобы гарантировать корректное завершение программы или повторение попытки инициализации. В этом примере ошибка инициализации обрабатывается в main, что позволяет программе корректно завершить работу или предпринять другие действия.
int main() {
try {
DatabaseConnection db("connection_string");
} catch (const std::exception& e) {
std::cerr << "Initialization failed: " << e.what() << std::endl;
// Дополнительные действия по обработке ошибки
}
return 0;
}{}
Ставь 👍 и забирай 📚 Базу знанийstd::map и std::unordered_map – оба являются ассоциативными контейнерами в C++, но они устроены по-разному и имеют разные скорости работы.
🚩Как устроены контейнеры?
🟠`std::map` – красно-чёрное дерево (сбалансированное BST)
Все ключи хранятся упорядоченно.
Операции insert, erase, find выполняются за O(log n).
Упорядоченность важна, если требуется поиск в диапазоне (lower_bound, upper_bound).
#include <map>
#include <iostream>
int main() {
std::map<int, std::string> m;
m[2] = "B";
m[1] = "A";
m[3] = "C";
for (const auto& [key, value] : m) {
std::cout << key << " -> " << value << '\n';
}
}{}
Вывод (отсортирован!)
1 -> A
2 -> B
3 -> C {}
🟠`std::unordered_map` – хеш-таблица
Нет сортировки, но поиск выполняется быстро – O(1) (в среднем).
Использует хеш-функцию, поэтому доступ к элементам зависит от хеширования.
Может быть медленным при коллизиях (O(n) в худшем случае).
#include <unordered_map>
#include <iostream>
int main() {
std::unordered_map<int, std::string> um;
um[2] = "B";
um[1] = "A";
um[3] = "C";
for (const auto& [key, value] : um) {
std::cout << key << " -> " << value << '\n';
}
}{}
Вывод (порядок непредсказуем!)
2 -> B
1 -> A
3 -> C {}
🚩Когда использовать `map`, а когда `unordered_map`?
🟠Используйте `std::map`, если:
Нужно сортированное хранение ключей.
Требуется поиск по диапазону (lower_bound, upper_bound).
Хеш-функция для ключей сложна или недоступна (например, std::pair без кастомного хешера).
Ставь 👍 и забирай 📚 Базу знаний
#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;
}{}
Ставь 👍 и забирай 📚 Базу знанийstd::mutex в качестве локальной переменной может привести к различным проблемам, особенно если он используется для синхронизации доступа к общим данным. Локальный std::mutex будет уничтожен при выходе из области видимости, что нарушит работу других потоков, ожидающих блокировку или разблокировку.
🚩Проблемы с локальным `std::mutex`
🟠Жизненный цикл локального `std::mutex`
Локальная переменная уничтожается при выходе из функции или блока, в котором она объявлена. Это может привести к неопределенному поведению, если другие потоки все еще используют этот мьютекс.
🟠Неопределенное поведение
Уничтожение мьютекса, который все еще заблокирован, может привести к неопределенному поведению программы и потенциальным сбоям.
🚩Решение проблемы
Чтобы исправить проблему, нужно гарантировать, что std::mutex имеет более длительный срок жизни и доступен всем потокам, которые его используют.
🚩Правильные подходы
🟠Использование глобального или статического мьютекса
Если мьютекс используется для защиты глобальных или статических данных, сделайте его также глобальным или статическим.
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
void sharedFunction() {
std::lock_guard<std::mutex> lock(mtx);
// Доступ к общим данным
std::cout << "Thread " << std::this_thread::get_id() << " is running\n";
}
int main() {
std::thread t1(sharedFunction);
std::thread t2(sharedFunction);
t1.join();
t2.join();
return 0;
}{}
🟠Член класса
Если мьютекс используется для защиты данных объекта, объявите его членом класса.
#include <iostream>
#include <thread>
#include <mutex>
class SharedResource {
private:
std::mutex mtx;
int data;
public:
void increment() {
std::lock_guard<std::mutex> lock(mtx);
++data;
std::cout << "Data: " << data << " from thread " << std::this_thread::get_id() << "\n";
}
};
int main() {
SharedResource resource;
std::thread t1(&SharedResource::increment, &resource);
std::thread t2(&SharedResource::increment, &resource);
t1.join();
t2.join();
return 0;
}{}
🟠Использование умных указателей
Если мьютекс должен иметь динамическую продолжительность жизни, используйте умные указатели, такие как std::shared_ptr или std::unique_ptr.
#include <iostream>
#include <thread>
#include <mutex>
#include <memory>
void sharedFunction(std::shared_ptr<std::mutex> mtx) {
std::lock_guard<std::mutex> lock(*mtx);
// Доступ к общим данным
std::cout << "Thread " << std::this_thread::get_id() << " is running\n";
}
int main() {
auto mtx = std::make_shared<std::mutex>();
std::thread t1(sharedFunction, mtx);
std::thread t2(sharedFunction, mtx);
t1.join();
t2.join();
return 0;
}{}
Ставь 👍 и забирай 📚 Базу знанийОтзывы канала
- Добавлен: Сначала новые
- Добавлен: Сначала старые
- Оценка: По убыванию
- Оценка: По возрастанию
Каталог Телеграм-каналов для нативных размещений
C/C++ | Вопросы собесов — это Telegam канал в категории «Интернет технологии», который предлагает эффективные форматы для размещения рекламных постов в Телеграмме. Количество подписчиков канала в 4.3K и качественный контент помогают брендам привлекать внимание аудитории и увеличивать охват. Рейтинг канала составляет 5.2, количество отзывов – 1, со средней оценкой 5.0.
Вы можете запустить рекламную кампанию через сервис Telega.in, выбрав удобный формат размещения. Платформа обеспечивает прозрачные условия сотрудничества и предоставляет детальную аналитику. Стоимость размещения составляет 3636.36 ₽, а за 3 выполненных заявок канал зарекомендовал себя как надежный партнер для рекламы в TG. Размещайте интеграции уже сегодня и привлекайте новых клиентов вместе с Telega.in!
Вы снова сможете добавить каналы в корзину из каталога
Комментарий