1409 просмотров
от 4 июня 2024
C/C++

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

Вопросы и ответы с собеседований C++ разработчиков. Определение указателей, встраивание C-кода, структуры, классы, методы, ключевое слово const, потребление и выделение памяти, обработка исключений, практические вопросы и многое другое.

1

Какой код выполняется до main?

Конструкторы глобальных объектов.

Комментарии
0/3000
2

Как вызвать функцию C в коде C++?

Это возможно, если использовать extern «C»: //C code void func(int i) { //code } void print(int i) { //code } //C++ code extern "C"{ void func(int i); void print(int i); } void myfunc(int i) { func(i); print(i); }

Комментарии
0/3000
3

Что такое указатель?

Указатель — это переменная, которая хранит адрес памяти объекта. Указатели широко используются в C и C++ для трех основных целей: 1. Для выделения новых объектов в куче, 2. Для передачи функций другим функциям 3. Для итерации элементов в массивах или других структурах данных.

Комментарии
0/3000
4

Раскажите про ключевое слово const

1. Позволяет задать константность объекта 2. Позволяет задать константность указателя 3. Позволяет указать, что данный метод не модифицирует члены класса, т.е. сохраняет состояние объекта Не можем изменить значение объекта: const int i = 1; i = 2; // error C3892: 'i' : you cannot assign to a variable that is const Не можем изменить указатель на объект: int i = 1; int* const j(&i); int k = 2; *j = k; // Ok j = &k; // error C3892: 'j' : you cannot assign to a variable that is const Не можем изменить члены класса: class Foo { private: int i; public: void func() const { i = 1; // error C3490: 'i' cannot be modified because it is being accessed through a const object } }; Дополнение: константный метод может изменять члены класса, если они объявлены как mutable.

Комментарии
0/3000
5

Как запретить копирование объекта?

Для того, чтобы запретить копирование объекта, нужно сделать private конструктор копирования и оператор =. class NonCopyable { public: NonCopyable(){} private: NonCopyable(NonCopyable&){} private: void operator=(const NonCopyable&){} }; NonCopyable a; NonCopyable b = a; // error C2248: 'NonCopyable::NonCopyable' : cannot access private member a = b; // error C2248: 'NonCopyable::operator =' : cannot access private member

Комментарии
0/3000
6

Зачем нужен модификатор virtual?

В C++ виртуальные функции позволяют поддерживать полиморфизм – одну из ключевых составляющих ООП. С его помощью в классах-потомках можно переопределять функции класса-родителя. Без виртуальной функции мы получаем «раннее связывание», а с ней – «позднее». То есть, какая реализация метода используется, определяется непосредственно во время выполнения и основывается на типе объекта с указателем на объект, из которого он построен.

Комментарии
0/3000
7

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

// Абстрактный класс class Foo { public: // Чисто виртуальный метод virtual void func() = 0; }; class Bar : public Foo { public: virtual void func() { } }; Foo f; // error C2259: 'Foo' : cannot instantiate abstract class Bar b; // Ok Чисто виртуальный метод — это метод, у которого отсутствует реализация. Абстрактный класс — это класс, имеющий хотя бы один чисто виртуальный метод. Как следствие, экземпляр подобного класса не может быть создан т. к. отсутствует реализация виртуального метода.

Комментарии
0/3000
8

Что такое виртуальный деструктор?

Чтобы избежать возможной утечки ресурсов или другого неконтролируемого поведения объекта, в логику работы которого включен вызов деструктора. Пример: class Base { public: virtual ~Base() { std::cout << "Hello from ~Base()" << std::endl; } }; class Derived : public Base { public: virtual ~Derived() { // Здесь могла бы быть очистка ресурсов std::cout << "Hello from ~Derived()" << std::endl; } }; Base *obj = new Derived(); delete obj; Вывод: Hello from ~Derived() Hello from ~Base() Без ключевого слова virtual у родительского класса Base деструктор порожденного класса не был бы вызван. Т.е. вызвался бы только ~Base().

Комментарии
0/3000
9

Что такое виртуальный конструктор?

Каверзный вопрос, который чаще всего задают именно после виртуальных деструкторов, дабы сбить кандидата с толку. Конструктор не может быть виртуальным, поскольку в нем нет никакого смысла: при создании объектов нет такой неоднозначности, как при их удалении.

Комментарии
0/3000
10

Разница между struct и class?

struct Foo { int i; }; class Bar { int i; }; Foo a; a.i = 1; // Ok Bar b; b.i = 1; // error C2248: 'Bar::i' : cannot access private member declared in class 'Bar' Практически ни в чём. В struct модификаторы доступа по умолчанию public, в class - private. Так же отличается и наследование по умолчанию, у structpublic, у classprivate.

Комментарии
0/3000
11

Разница между struct и union?

В struct все её члены хранятся одновременно, и к ним есть доступ. В union существует только один тип, который в данный момент используется. Обращение к остальным вызовет undefined behavior.

Комментарии
0/3000
12

Сколько памяти занимает произвольная структура?

Можно рассчитать по формуле: sizeof всех членов + остаток для выравнивания (по умолчанию выравнивание 4 байта) + sizeof указателя на vtable (если есть виртуальные функции) + указатели на классы предков, от которых было сделано виртуальное наследование (размер указателя * количество классов) Пример: struct Foo { int i; char a; }; int size = sizeof(Foo); // 8 байт, хотя int + char = 5. Все дело в дефолтном выравнивании равном 4, т.е. размер должен быть кратен блоку в 4 байта. // Установим выравнивание в 1 байт #pragma pack(push, 1) struct Foo { int i; char a; }; #pragma pack(pop) int size = sizeof(Foo); // 5 байт

Комментарии
0/3000
13

Различия std::array и std::vector?

std::array - это обёртка вокруг обычного массива фиксированного размера, а std::vector - это динамически расширяемый массив. std::array хранит свои данные в стековой памяти, а std::vector выделяет память в куче (динамическая память). У std::array в параметрах шаблона указывается тип хранимых элементов и размер массива. А у std::vector - тип элементов и ещё возможно указать аллокатор, который нужно использовать при выделении памяти (с помощью new). std::array чуть лучше по производительности в случае небольшого массива, чем std::vector, который содержит в себе несколько указателей, которые указывают на выделенный в куче кусок памяти.

Комментарии
0/3000
14

В чем отличие vector от deque?

У deque имеются методы push_front и pop_front. Но основное отличие в организации памяти: у vector она, как у обычного Си-массива, т.е. последовательный и непрерывный набор байт, а у deque это фрагменты с разрывами. За счет этого отличия vector всегда можно привести к обычному массиву или скопировать целиком участок памяти, но зато у deque операции вставки/удаления в начало быстрее (O(1) против O(n)), ввиду того, что не нужно перемещать остальные значения.

Комментарии
0/3000
15

В чем отличие malloc от new?

malloc — выделение блока памяти в стиле Си, опасное с точки зрения приведения типов (non-typesafe), т.к. возвращает void и требует обязательного приведения. new — выделение блока памяти и последующий вызов конструктора; безопасное с точки зрения приведения типов (typesafe), т.к. тип возвращаемого значения определен заранее.

Комментарии
0/3000
16

Различия delete и delete[]?

class Foo { }; Foo *pFoo = new Foo(); delete pFoo; Foo *pFooArray = new Foo[10](); delete[] pFoo; delete предназначен для уничтожения объектов, память под которые выделена при помощи new(). delete[] предназначен для объектов, выделенных при помощи оператора new[](). При неправильном использовании оператора delete (например, delete вместо delete[]) результат будет: undefined behavior

Комментарии
0/3000
17

Как добавить объект в качестве ключа в ассоциативные контейнеры?

struct Foo { int i; }; inline bool operator < (const Foo & lhs, const Foo & rhs) { return lhs.i < rhs.i; } std::set<Foo> data; Foo a, b, c; a.i = 7; b.i = 1; c.i = 6; data.insert( a ); data.insert( b ); data.insert( c ); // Теперь в data элементы находятся в последовательности 1 6 7 Т.к. значения в ассоциативных контейнерах хранятся отсортированными, объект должен реализовывать оператор сравнения <, а остальные операторы сравнения могут быть выражены через него.

Комментарии
0/3000
18

Что дают разные модификаторы при наследовании?

Изменяют зону видимости членов базового класса. При private наследовании protected и public члены становятся private. При protected наследовании public становится protected. А при public ничего не изменяется. class Base { public: int i; }; class Derived : private Base { // i теперь имеет модификатор доступа private }; class Derived2 : private Derived { public: Derived2() { i = 2; // error C2247: 'Base::i' not accessible because 'Derived' uses 'private' to inherit from 'Base' } }; Derived d; d.i; // error C2247

Комментарии
0/3000
19

Для чего используется ключевое слово volatile?

volatile int i = 1; // Независимо от прочего кода, данная переменная не будет оптимизирована. Для указания компилятору, что доступ к переменной может осуществляться из мест, неподконтрольных ему. А как следствие, что работу с данной переменной не нужно подвергать разного рода оптимизациям. Т.е. если volatile присутствует в каком-то условии, которое не меняется со временем, то компилятор может оптимизировать его, чтобы избежать ненужных проверок. При использовании volatile компилятор скорее всего не будет этого делать. while (1) { if(i == 1) { // Какой-то код, не изменяющий i } } // Если бы volatile отсутствовало, то компилятор мог бы переделать код на что-то аля: if(i == 1) // Нет необходимости проверять i все время, если и так известно, что оно не изменяется { while (1) { // Какой-то код, не изменяющий i } }

Комментарии
0/3000
20

Для чего используется вызов throw без аргументов?

try { //.... try { // Call something } catch(const std::exception& ) { // Make/Check something.. throw; // Пересылаем исключение следующему обработчику } //... } catch(const std::exception& e) { std::cout << e.what() << std::endl; } Для повторного возбуждения предыдущего исключения и направления его следующему обработчику.

Комментарии
0/3000
21

Как сгенерировать pure virtual function call исключение?

Нужно вызвать чисто виртуальный метод в конструкторе родительского класса, т.е. до создания дочернего, в котором этот метод реализован. Т.к. современный компилятор не даст это сделать напрямую, нужно будет использовать промежуточный метод. Пример: class Base { public: Base() { base_func(); } void base_func() { func(); // pure virtual function call exception } virtual void func() = 0; }; class Derived : public Base { public: virtual void func() { } };

Комментарии
0/3000
22

Какие нюансы у auto_ptr?

std::auto_ptr<Foo> a(new Foo); std::auto_ptr<Foo> b; b = a; // a больше не ссылается на Foo std::vector<std::auto_ptr<Foo>> v; v.push_back(b); // error C2558: class 'std::auto_ptr<_Ty>' : no copy constructor available or copy constructor is declared 'explicit' Так как данный умный указатель реализует подход разрушающего копирования, то при присвоении его другому умному указателю оригинальный потеряет свое значение. А так же его нельзя использовать в стандартных STL контейнерах. Также ввиду того, что в деструкторе auto_ptr вызывается оператор delete, нельзя хранить объекты, созданные при помощи new[](). Именно из-за этого нюанса boost предоставляет умные указатели в двух вариантах, для просто объекта и их коллекции, например, shared_ptr и shared_array.

Комментарии
0/3000
23

Бывает такое, что оператор new не выделяет память?

Да, бывает, когда new передаётся указатель на уже выделенную память (например, с помощью malloc). Это называется placement new. И оператор new без изменения возвращает второй параметр - указатель. Это используется для создания объектов в выделенном "хранилище" или после malloc. Пример: #include <iostream> struct A{ A(){ std::cout << "I'm constructor\n"; } ~A(){ std::cout << "I'm destructor\n"; } }; int main() { void *pv = malloc(sizeof(A)); // или char *pv = new char[sizeof(A)]; A *pa = new(pv) A; std::cout << std::hex << reinterpret_cast<A*>(pv) << " = " << pa << '\n'; pa->~A(); free(pv); // или delete[] pv; pv = nullptr; } Важно! В этом случае деструктор нужно вызывать самостоятельно!

Комментарии
0/3000
24

Когда вызывается конструктор копирования, а когда копирующий оператор присваивания?

Конструктор копирования вызывается, когда объект ещё не создан и на основе другого объекта создается текущий, а копирующий оператор присваивания - когда объект уже создан и происходит копирование всех элементов из одного объекта в другой. Пример: #include <iostream> class A{ public: A(){ std::cout << "Constructor\n"; } A(const A&){ std::cout << "Copy constructor\n"; } A& operator=(const A&){ std::cout << "Copy assignment operator\n"; return *this; } ~A(){ std::cout << "Destructor\n"; } }; int main() { A a1; A a2; A a3 = a1; // вызывается копирующий конструктор A(const A&), так как объект a3 не создан a2 = a1; // вызывается оператор копирующего присваивания: A& operator= } Constructor Constructor Copy constructor Copy assignment operator Destructor Destructor Destructor

Комментарии
0/3000
25

Можно ли использовать ссылку при возвращении из функции?

Нельзя: - никогда нельзя возвращать ссылку на локальную переменную, созданную внутри функции, потому что она разрушится сразу после выхода из функции; - нельзя возвращать ссылку на выделенную память, потому что её нельзя будет очистить. Можно: - когда возвращается константная ссылка на член класса; - когда вы пишете оператор <<, оператор = и оператор индексирования, они должны возвращать ссылку на объект. Возвращать ссылку - это хорошая практика, потому что не вызывается лишний копирующий конструктор для возвращаемого значения, но иногда лучше и правильнее возвращать по значению.

Комментарии
0/3000
26

Что будет выведено на экран?

#include <iostream> int a=4; int &f(int x){ a = a + x; return a; } int main(void){ int t = 5; std::cout << f(t) << std::endl; // 9 f(t) = 20; std::cout << f(t) << std::endl; // 25 t = f(t); std::cout << f(t) << std::endl; // 60 } В первом случае на печать выведется 9, потому что функция f принимает параметр t = 5, значение которого внутри функции прибавляется к глобальной переменной a. Теперь a = 9. А f возвращает ссылку на неё и печатается значение a, то есть 9. Далее не важно, что происходит внутри функции, важно, что возвращаемой ссылке на a присваивается 20. Значит, a = 20. Переменная t не поменяла своё значение, так как в функцию она передается по значению, а не по ссылке. Затем опять вызываем f(5), при этом a = 20. Выполняем a = 20 + 5, на печати увидим число 25. Теперь присваиваем t значение a (в этот момент a = 25 + 5), значит, t станет равно 30. И, наконец, последняя печать. Вызываем f(30). Выполняется a = 30 + 30 и возвращается значение 60.

Комментарии
0/3000
Вопросники по C/C++
C/C++
26 вопросов
Вопросы и ответы с собеседований по C++
1409 просмотров
C/C++
17 вопросов
Вопросы и ответы с собеседований по C
1162 просмотра
Хотите заработать на создании вопросников?
Стать редактором
Смежные категории
Базы данных
10 вопросов
Вопросы с собеседований о репликации баз данных
1457 просмотров
Computer Science
12 вопросов
Вопросы с собеседований про операционные системы
1061 просмотр
Базы данных
60 вопросов
Вопросы и ответы с собеседований по SQL
2377 просмотров
Python
32 вопроса
Вопросы и ответы с собеседований по Python
2449 просмотров
Computer Science
13 вопросов
Вопросы и ответы с собеседований про ООП
1230 просмотров
Docker
7 вопросов
Коллекция полезных команд для Docker
1321 просмотр
Рекомендуем
Computer Science
13 вопросов
Вопросы и ответы с собеседований про ООП
1230 просмотров
Computer Science
28 вопросов
Объяснение паттернов проектирования с примерами
1424 просмотра
Computer Science
11 вопросов
Вопросы и ответы про интернет-протоколы
1464 просмотра
Computer Science
15 вопросов
Вопросы и ответы с собеседований по DDD
1526 просмотров
Git
20 вопросов
Вопросы и ответы с собеседований по Git
1783 просмотра
Базы данных
60 вопросов
Вопросы и ответы с собеседований по SQL
2377 просмотров
Другие разделы

Лента

Активность пользователей Девстанции

Перейти к ленте

Лидеры

Рейтинг самых результативных пользователей сообщества

Перейти к лидерам

Треды

Общение по интересам и связь с разработчиками

Перейти к тредам

Задачи

Решение алгоритмических задач с собеседований

Перейти к задачам

Вопросы

Ответы на вопросы с технических собеседований

Вы находитесь здесь

Викторины

Интерактивные викторины по вопросам с собеседований

Перейти к викторинам
Мы в Telegram
Новости проекта, общение с разработчиками, общение по интересам - присоединяйтесь!