С++ для интервью
37 subscribers
10 photos
Готовься к собеседованиям по C++ эффективно: реальные вопросы, типичные HR уловки, примеры кода и объяснения.
Каждый пост — один шаг ближе к офферу 💼
Download Telegram
Что делает такой шаблон RAII-защитника?
🧩 SOLID-принципы C++

Что такое SOLID и для чего он необходим?

1️⃣ S — Single Responsibility

Пояснение:
Класс делает узконаправленную задачу
Пример: Logger не должен писать в базу.

2️⃣ O — Open/Closed

Пояснение: Код открыт для расширения, но закрыт для модификации.
Пример: Использование виртуальных функций, шаблонов, полиморфизма.

3️⃣ L — Liskov Substitution

Пояснение:
Объекты подклассов можно использовать вместо базовых.
Пример: Если Derived ломает контракт Base, нарушен LSP.

4️⃣ I — Interface Segregation

Пояснение: Лучше несколько маленьких интерфейсов, чем один общий.

5️⃣ D — Dependency Inversion

Пояснение: Зависимости — от абстракций, а не от конкретных реализаций.
Пример: Использование родительского класса для создания экземпляра — unique_ptr<IService> вместо unique_ptr<MyService>.

⚙️ Пример использования SOLID

struct IShape {
virtual double area() const = 0;
virtual ~IShape() = default;
};

class Circle : public IShape {
double r_;
public:
explicit Circle(double r) :
r_(r) {}

double area() const override
{
return 3.14 * r_ * r_;
}
};


— SRP: Circle отвечает только за площадь круга.
— OCP: можно добавить Rectangle, не меняя IShape.
— LSP: любой IShape заменим на Circle.
— ISP: интерфейс компактный.
— DIP: зависимости через абстракции.

Подводя итог, SOLID — набор основных принципов, позволяющих писать читаемый, тестируемый и расширяемый C++ код.

#cpp_tips
🧬 CRTP (Curiously Recurring Template Pattern)

Как использовать шаблоны, а не виртуальность, чтобы дать базовому классу знать о потомке?


CRTP — это шаблон, где класс-наследник передаёт себя в качестве параметра шаблона базовому классу.

template <typename Derived>
class Base
{
public:
void interface()
{
static_cast<Derived*>
(this)->implementation();
}
};


А наследник выглядит так:

class Derived : public Base<Derived>
{
public:
void implementation()
{
std::cout << "Derived func\n";
}
};


⚙️ Как это работает

Когда ты вызываешь:

Derived d;
d.interface();


Базовый класс вызывает метод implementation() у конкретного типа Derived,
при этом без виртуальных функций — всё статически известно на этапе компиляции.

🚀 Где это полезно:

— Mixin — добавление поведения к классам без виртуальности.

— Policy-based design — подмена стратегий через шаблонные параметры.

— Статический полиморфизм — альтернатива virtual, но без runtime overhead.

— Ограничение интерфейса наследников — например, через enable_if и SFINAE.


🧠 Пример Mixin с CRTP

template <typename Derived>
class Printable
{
public:
void print() const
{
std::cout <<
static_cast<const Derived*>
(this)->toString();
}
};

class Point : public Printable<Point>
{
public:
Point(int x, int y) : x_(x), y_(y) {}
std::string toString() const
{
return std::to_string(x_) + ","
+ std::to_string(y_);
}
private:
int x_, y_;
};


Point p(2, 3);
p.print(); // 2,3


— Без virtual
— Без динамической памяти

— Гибко и эффективно

В итоге имеем:
CRTP — способ реализовать полиморфизм, заменив виртуальные вызовы статическим связыванием на этапе компиляции.

#cpp_tips
🧩 Тест 1 — Как работает вывод типов шаблона
Что выведет программа
Anonymous Quiz
0%
int
0%
double
100%
Ошибка компиляции
0%
auto
⚙️ Тест 2 — Передача по ссылке и вывод типов
Какой тип будет у T
Anonymous Quiz
0%
int
100%
const int
0%
int &
0%
const int &
🧩 Тест 3 — Как работает частичная специализация
⚙️ Type Traits: когда типы становятся логикой

Примеры Type Traits
template<typename T>
struct is_pointer
{
static constexpr bool value = false;
};

template<typename T>
struct is_pointer<T*>
{
static constexpr bool value = true;
};

assert(is_pointer<int*>::value);


🧠 Type Traits — шаблонные конструкции, которые предоставляют информацию о свойствах типов во время компиляции
Используются в std::enable_if, concepts, std::move.
Какой trait определяет, является ли тип числовым?
Anonymous Poll
0%
std::is_arithmetic
0%
std::is_integral
100%
std::is_numeric
🎯 Policy-based Design: шаблонная настройка поведения

Пример реализации:
template<typename ThreadPolicy, typename LogPolicy>
class Engine : private ThreadPolicy, private LogPolicy
{
public:
void run()
{
this->log("Starting...");
this->lock();
this->unlock();
}
};


✅ Вместо наследования от суперкласса составляем объект из политик, что позволяет легко менять поведение без лишнего кода.
Что проще изменить в Policy-based подходе?
Anonymous Poll
0%
Интерфейс базового класса
0%
Структуру иерархии
100%
Поведение
Expression Templates

Или магия ленивых выражений

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

Таким образом, шаблоны выражений позволяют программистам обойти обычный порядок вычисления на языке C++ и добиться оптимизации, такой как объединение циклов.

Пример:
template<typename L, typename R>
struct Add
{
L const& l; R const& r;
auto eval() const
{
return l.eval() + r.eval();
}
};

struct Value
{
double v;
auto eval() const
{
return v;
}
};


🧠 Такие паттерны лежат в основе библиотек Eigen, Blitz++, Armadillo. Они предотвращают создание временных объектов — всё оптимизируется на этапе компиляции.
Static Interface (Concepts)

Static Interface: контракты времени компиляции

template<typename T>
concept Drawable = requires(T obj)
{
obj.draw();
};

template<Drawable T>
void render(T obj)
{
obj.draw();
}


🧠 Concepts — это compile-time контракты.
Компилятор гарантирует, что тип соответствует интерфейсу.
Это безопасный и быстрый способ добавления ограничений к создаваемым объектам
🌳 std::map vs 🖥 std::unordered_map

Две ассоциативные структуры, два разных назначения.

map

— Красно-чёрное дерево
— Операции за O(log n)
— Сохраняет сортировку ключей

unordered_map

— Хеш-таблица
— Амортизированная O(1)
— Нет сортировки
Но, может переполняться и ребалансироваться

std::unordered_map<std::string, int> id;
id["Alice"] = 5;


💡 Если нужны сортированные ключи — map. Если нужна скорость — unordered_map.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤓 std::algorithm

STL-алгоритмы работают над итераторами, а не над контейнерами — поэтому они универсальны.

std::vector<int> v = {1, 2, 3, 4};

auto it = std::find(v.begin(), v.end(), 3);

std::reverse(v.begin(), v.end());

std::sort(v.begin(), v.end());


Полезные алгоритмы, которые могут быть полезны:

std::partition — разбивает по условию

std::unique — убирает дубликаты (но не меняет размер!)

std::any_of, std::none_of, std::all_of

💡 Комбинируя алгоритмы и лямбды, можно писать универсальный и масштабируемый код.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4