Решение: Потокобезопасный Singleton на C++11
Лучший подход: Magic Statics (Scott Meyers)
Начиная с C++11 стандарт гарантирует потокобезопасную инициализацию локальных статических переменных. Это самое элегантное и надёжное решение:
template <typename T>
class Singleton {
public:
static T& getInstance() {
static T instance;
return instance;
}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
Singleton(Singleton&&) = delete;
Singleton& operator=(Singleton&&) = delete;
protected:
Singleton() = default;
~Singleton() = default;
};
Использование
class Logger : public Singleton<Logger> {
public:
void log(const std::string& message) {
std::cout << "[LOG] " << message << std::endl;
}
protected:
friend class Singleton<Logger>;
Logger() { std::cout << "Logger initialized" << std::endl; }
};
Решение: Пул потоков (Thread Pool)
Архитектура
Пул потоков состоит из:
Ключевая идея: рабочие потоки циклически ждут задач, выполняют их и кладут результат в future.
Полная реализация
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <functional>
#include <future>
#include <vector>
#include <memory>
Решение: Реализация LRU Cache
Описание подхода
Для достижения требуемой O(1) сложности для обеих операций используем комбинацию двусвязного списка (std::list) и хеш-таблицы (std::unordered_map):
Этот подход позволяет:
Реализация
#include <unordered_map>
#include <list>
Решение: Потокобезопасная очередь
Архитектура
Потокобезопасная очередь (thread-safe queue) комбинирует:
Ключевая идея: потребители блокируются на condition_variable и просыпаются, когда producer добавляет элемент.
Полная реализация
#include <queue>
#include <mutex>
#include <condition_variable>
#include <memory>
Решение: Задача про заправки (Gas Station)
Ключевое наблюдение
Эта задача решается одним проходом благодаря важному свойству:
Если общее количество топлива >= общего расхода, решение всегда существует. Нужно только найти правильную стартовую точку.
Стратегия поиска стартовой точки:
Полное решение
Решение: Поиск дубликатов с условием на расстояние
Подход: Скользящее окно (Sliding Window) + Хеш-таблица
Идея состоит в том, что мы поддерживаем окно размера k+1, содержащее только элементы, которые могут образовать пару. Если в этом окне два элемента одинаковы — мы нашли ответ.
Ключевое наблюдение: Если два индекса i и j удовлетворяют условию |i - j| <= k, то в момент обработки индекса j, индекс i находится в окне [j-k, j].
Реализация
#include <vector>
#include <unordered_set>
Решение: Реализация умного указателя shared_ptr
Концепция
shared_ptr использует подсчёт ссылок (reference counting) для автоматического управления памятью:
Полная реализация
#include <iostream>
#include <utility>
Решение: Генератор идентификаторов на C++
Анализ системы счисления
Это система с переменным основанием: каждый уровень состоит из пар (буква, цифра):
Реализация
Класс хранит счётчик и конвертирует его в ID:
Решение: Многопоточный TCP-сервер на C++
Архитектура
Сервер состоит из двух основных частей:
Полная реализация
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <atomic>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
Решение: Анализ проблем с dynamic_cast
Проблема 1: dynamic_cast требует виртуального деструктора
Проблема: Базовый класс не имеет виртуального деструктора:
class Base {
public:
void print() { ... } // ❌ деструктор не виртуальный
};
Что произойдёт:
delete b1 через Base*, разрушится только Base (Derived деструктор не вызовется)Вывод кода БЕЗ исправлений:
Derived only
Segmentation Fault / Undefined Behavior
Проблема 2: Отсутствие проверки результата dynamic_cast
Проблема: После dynamic_cast не проверяется, вернул ли он nullptr:
void process(Base* ptr) {
Derived* d = dynamic_cast<Derived*>(ptr);
d->derivedOnly(); // ❌ если d == nullptr, это UB!
}
Решение: Вычисление Reverse Polish Notation (RPN)
Принцип работы
Обратная польская нотация (RPN) — это постфиксная запись, где операторы идут ПО СЛЕ операндов. Её главное преимущество — вычисление слева направо без скобок и приоритетов.
Алгоритм:
Реализация
#include <vector>
#include <string>
#include <stack>
#include <cctype>
#include <stdexcept>
Решение: Бесконечный цикл с unsigned char
Анализ проблемы
Исходный код имеет критическую ошибку переполнения типа.
Вычисление выражения
unsigned char half_limit = 150;
for (unsigned char i = 0; i < 2 * half_limit; ++i)
Важно: 2 * half_limit вычисляется как выражение, а не как присваивание!
Шаг 1: Integer promotion
2 * half_limit срабатывает integer promotionШаг 2: Сравнение в условии цикла
i < 300Результат: Цикл выполнит 256 итераций (i от 0 до 255):
Пошаговое объяснение с примерами
Решение: Светофор (State Machine)
Анализ требований
Необходимо реализовать управление светодиодным светофором с тремя состояниями и чёткими временными переходами. Это классическая задача на паттерн State Machine.
Ключевые требования:
Реализация паттерна State Machine
#include <iostream>
#include <functional>
#include <chrono>
#include <thread>
#include <mutex>
enum class LightState { RED, YELLOW, GREEN };