Как пройти собеседование по C++: топ вопросов и ответы

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Как пройти собеседование по C++: топ вопросов с примерами кода

Собеседование по C++ — одно из самых сложных испытаний для разработчика. Этот язык сочетает мощь низкоуровневого программирования с современными парадигмами ООП и шаблонов. Чтобы успешно пройти интервью, нужно не просто знать синтаксис, но и понимать, как работает память, компилятор и стандартная библиотека. В этой статье мы разберём топ-10 вопросов, которые чаще всего задают на собеседованиях по C++, с подробными ответами и примерами кода.

1. Что такое RAII и зачем он нужен?

RAII (Resource Acquisition Is Initialization) — одна из ключевых идиом C++. Суть проста: ресурс (память, файл, мьютекс) захватывается в конструкторе объекта и освобождается в деструкторе. Это гарантирует корректную очистку даже при исключениях.

Пример неправильного кода (без RAII):

void process() {
    int* ptr = new int(42);
    // ... код, который может выбросить исключение
    delete ptr; // утечка, если исключение случилось до delete
}

Пример с RAII (используем std::unique_ptr):

#include <memory>

void process() {
    std::unique_ptr<int> ptr = std::make_unique<int>(42);
    // ... код с исключениями
    // ptr автоматически удалится при выходе из области видимости
}

Почему это спрашивают: RAII — основа безопасного управления ресурсами в C++. Без него невозможно писать надёжный код.

2. Чем отличается виртуальная функция от чисто виртуальной?

Виртуальная функция (virtual) может быть переопределена в производном классе, но имеет реализацию в базовом. Чисто виртуальная функция (= 0) не имеет реализации в базовом классе и делает класс абстрактным — нельзя создать объект такого класса.

Пример:

class Base {
public:
    virtual void foo() { std::cout << "Base foo\n"; }   // виртуальная
    virtual void bar() = 0;                               // чисто виртуальная
};

class Derived : public Base {
public:
    void foo() override { std::cout << "Derived foo\n"; }
    void bar() override { std::cout << "Derived bar\n"; }
};

Дополнительный вопрос: Как работает vtable? Компилятор создаёт таблицу виртуальных функций для каждого класса с виртуальными методами. Каждый объект содержит указатель на эту таблицу (vptr). При вызове виртуальной функции происходит динамическая диспетчеризация через vtable.

3. Какие умные указатели есть в C++ и когда их использовать?

В C++11 появились три типа умных указателей:

  • std::unique_ptr — единоличное владение. Нельзя копировать, только перемещать. Идеален для ресурсов с одним владельцем.
  • std::shared_ptr — разделяемое владение. Использует счётчик ссылок. Подходит, когда несколько объектов должны владеть одним ресурсом.
  • std::weak_ptr — не владеет ресурсом, но может наблюдать за shared_ptr. Нужен для разрыва циклических ссылок.

Пример использования unique_ptr:

#include <memory>

class MyClass { /* ... */ };

void example() {
    auto ptr = std::make_unique<MyClass>();
    // auto ptr2 = ptr; // Ошибка! Нельзя копировать
    auto ptr3 = std::move(ptr); // ОК, перемещение
}

Пример shared_ptr и weak_ptr для предотвращения циклических ссылок:

struct Node {
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> prev; // weak_ptr, чтобы избежать цикла
};

4. Что такое move-семантика и std::move?

Move-семантика позволяет «перемещать» ресурсы из одного объекта в другой вместо копирования. Это особенно полезно для временных объектов или когда копирование дорого (например, std::string или std::vector).

Пример до C++11 (копирование):

std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = v1; // глубокое копирование всех элементов

Пример с move-семантикой:

std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = std::move(v1); // перемещение: v1 теперь пуст

Важно: std::move не перемещает сам объект. Он просто преобразует lvalue в rvalue-ссылку, что позволяет вызвать конструктор перемещения или оператор присваивания перемещением.

Реализация своего класса с поддержкой move:

class MyBuffer {
public:
    MyBuffer(size_t size) : data_(new int[size]), size_(size) {}
    
    // Конструктор перемещения
    MyBuffer(MyBuffer&& other) noexcept 
        : data_(other.data_), size_(other.size_) {
        other.data_ = nullptr;
        other.size_ = 0;
    }
    
    ~MyBuffer() { delete[] data_; }
private:
    int* data_;
    size_t size_;
};

5. Как работает полиморфизм в C++?

Полиморфизм в C++ бывает двух видов:

  • Компиляционный (статический) — через перегрузку функций и шаблоны. Выбор версии происходит на этапе компиляции.
  • Динамический (run-time) — через виртуальные функции и наследование. Выбор происходит во время выполнения через vtable.

Пример динамического полиморфизма:

class Animal {
public:
    virtual void speak() const { std::cout << "Animal sound\n"; }
};

class Dog : public Animal {
public:
    void speak() const override { std::cout << "Woof!\n"; }
};

class Cat : public Animal {
public:
    void speak() const override { std::cout << "Meow!\n"; }
};

void makeSound(const Animal& animal) {
    animal.speak(); // динамическая диспетчеризация
}

int main() {
    Dog dog;
    Cat cat;
    makeSound(dog); // Woof!
    makeSound(cat); // Meow!
}

6. Что такое виртуальный деструктор и зачем

Блоги

Рекомендаций по книгам