P
prepair.app
Пройти интервью →
EnglishУкраїнськаРусский
🔷

Вопросы на собеседовании C++

Собеседование по C++ проверяет три вещи: понимаете ли вы объектную модель, можно ли доверить вам память и знаете ли вы, чего стоит стандартная библиотека. Ниже вопросы, которые задают чаще всего, каждый с эталонным ответом.

Junior · без опыта / до 1 годаMiddle · 2–4 года опытаSenior · 5+ лет опыта

Что спрашивают

Память, указатели и RAII
ООП, виртуальные функции и vtable
Move-семантика и правило пяти
Шаблоны и метапрограммирование
Контейнеры STL и сложности
Многопоточность и гонки данных

10 реальных вопросов с ответами

К каждому вопросу — эталонный ответ, с которым можно сравнить свой.

1

Чем указатель отличается от ссылки?

Ответ

Ссылка привязывается при создании и переприсвоена быть не может; указатель бывает нулевым, переприсваивается и имеет собственный адрес и арифметику. На практике ссылку берут, когда объект обязан существовать, а указатель — когда отсутствие является законным состоянием. Ссылка не отдельный объект, поэтому нет ни ссылок на ссылки, ни массивов из них.

2

Что такое RAII?

Ответ

Захват ресурса есть инициализация: ресурсом владеет объект, захват происходит в конструкторе, освобождение — в деструкторе. Поскольку деструкторы выполняются на всех путях выхода, включая раскрутку стека исключением, ресурс невозможно потерять из-за раннего return, который кто-то добавил позже. Все умные указатели, лок-гарды и файловые потоки стандартной библиотеки — это она.

3

`unique_ptr`, `shared_ptr`, `weak_ptr` — когда что?

Ответ

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

4

Зачем базовому классу виртуальный деструктор?

Ответ

Потому что удаление производного объекта через указатель на базовый без него — неопределённое поведение, а на практике деструктор наследника просто не выполняется и его поля утекают. Правило такое: если от класса собираются наследоваться и удалять полиморфно, деструктор виртуальный. Если класс не задуман базовым, деструктор ему не нужен, а добавленный создаёт vtable, которой не было.

5

Что такое `vptr` и `vtable`?

Ответ

У класса с виртуальными функциями появляется таблица указателей на функции — vtable, одна на класс, — а каждый объект хранит скрытый указатель на неё, vptr. Виртуальный вызов читает vptr, индексирует таблицу и прыгает. Отсюда и лишний указатель в размере объекта, и косвенность вызова, и то, что компилятор способен девиртуализовать вызов лишь когда может доказать динамический тип.

6

Чем стек отличается от кучи?

Ответ

Стек выделяется и освобождается сдвигом указателя при входе и выходе из области видимости: он быстрый, непрерывный и дружественный кэшу, но ограничен и привязан к времени жизни. Куча выделяется по запросу, собственных правил времени жизни не имеет и стоит реального вызова аллокатора плюс риска фрагментации. По умолчанию берут стек, а в кучу идут, когда размер неизвестен или объект должен пережить область видимости.

7

Что такое move-семантика и что делает `std::move`?

Ответ

Move-семантика позволяет передать ресурс вместо копирования: move-конструктор вектора забирает указатель на буфер, а не выделяет память и копирует элементы. Сам std::move ничего не перемещает — это приведение к rvalue-ссылке, которое делает объект пригодным для перемещения. Переместится ли что-то, зависит от наличия у типа move-конструктора, а объект-источник остаётся валидным, но с неопределённым содержимым.

8

Что такое неопределённое поведение и чем оно опасно?

Ответ

Поведение, на которое стандарт не накладывает требований: выход за границы массива, разыменование нуля, переполнение знакового, некорректная работа с перемещённым объектом, гонка данных. Опасно оно именно тем, что часто выглядит работающим: компилятор вправе считать, что этого не случается, и оптимизировать исходя из этого, поэтому тот же код ломается после постороннего изменения или другого уровня оптимизации. Ищут его санитайзерами.

9

Когда выбирают `std::vector`, а когда `std::list`?

Ответ

Почти всегда вектор. vector непрерывен, поэтому обход дружелюбен к кэшу и предвыборке, а list гоняется за указателем на каждом элементе и платит выделение на узел. list выигрывает, только когда вы постоянно вставляете в середину или сплайсите и держите итераторы, которые обязаны оставаться валидными, — и даже тогда стоит померить, потому что на современном железе локальность обычно бьёт лучшую асимптотику.

10

Что такое гонка данных и как её избежать?

Ответ

Два потока обращаются к одной ячейке памяти, хотя бы один пишет, и между ними нет синхронизации — это неопределённое поведение, а не просто неверный результат. Избегают её, не разделяя изменяемое состояние, либо защищая его мьютексом, либо делая доступ атомарным. ThreadSanitizer находит большинство таких мест, и это надёжнее рассуждений о расписании, которое вы не можете воспроизвести.

🦎

Прочитать ответы недостаточно

На собеседовании ты говоришь вслух под давлением. Кем задаст эти же вопросы, оценит каждый ответ и покажет, что именно подтянуть.

Пройти интервью C++ →
Бесплатно · 3 интервью в месяц

Стоит прочитать

Все статьи →

Другие специализации

🔍QA Manual🤖QA AutomationJava Backend🐍Python Backend🐘PHP Backend🦫Go Backend🟢Node.js Backend💎Ruby on Rails🟨JavaScript⚛️React Frontend💚Vue Frontend🅰️Angular FrontendNext.js🍏iOS (Swift)🟩Android (Kotlin)⚙️DevOps / SRE🗄️Data Engineer📈Business Analyst🎯Product Manager📋Project Manager🎨UI/UX Designer📣Marketing