TGViewer
Channel Public Channel
C/C++ | Вопросы собесов

C/C++ | Вопросы собесов

@easy_c_plus

Сайт: https://easyoffer.ru/
Все каналы: t.me/+xGeAw6ckJ4liYzQy

Контакт для рекламы: @sendme_ads
Subscribers
4.2K
Photos
36
Videos
1
Links
1.6K

Showing posts older than #2242 · Back to latest

Older Posts 20 shown
Post #2239 587
🤔 Что известно о ключевом слове override?

Ключевое слово override используется для явного указания, что метод переопределяет виртуальный метод базового класса. Оно предотвращает ошибки, связанные с неправильным именованием или сигнатурой методов.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 👍 2
Post #2237 574
🤔 Какие есть виды полиморфизма?

В программировании, включая C++, полиморфизм (многоформенность) – это способность объекта или функции принимать разные формы. Полиморфизм является ключевой концепцией объектно-ориентированного программирования (ООП).

🚩Компиляторный (статический) полиморфизм

Этот вид полиморфизма реализуется во время компиляции. Он достигается с помощью перегрузки функций (function overloading) и перегрузки операторов (operator overloading).

🟠Перегрузка функций
В перегрузке функций одна функция имеет несколько определений с разными параметрами.
#include <iostream>

void print(int value) {
std::cout << "Целое число: " << value << std::endl;
}

void print(double value) {
std::cout << "Вещественное число: " << value << std::endl;
}

void print(const std::string& value) {
std::cout << "Строка: " << value << std::endl;
}

int main() {
print(42); // Вызов функции для int
print(3.14); // Вызов функции для double
print("Привет!"); // Вызов функции для строки
return 0;
}


🟠Перегрузка операторов
Перегрузка операторов позволяет определить, как стандартные операторы работают с пользовательскими типами данных.
#include <iostream>

class Complex {
double real, imag;
public:
Complex(double r, double i) : real(r), imag(i) {}

Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}

void display() const {
std::cout << real << " + " << imag << "i" << std::endl;
}
};

int main() {
Complex c1(1.0, 2.0), c2(3.0, 4.0);
Complex c3 = c1 + c2; // Используется перегрузка оператора +
c3.display(); // Вывод: 4 + 6i
return 0;
}


🚩Рантаймный (динамический) полиморфизм

Этот вид полиморфизма проявляется во время выполнения программы. Реализуется с использованием виртуальных функций и наследования.

Виртуальные функции
#include <iostream>

class Animal {
public:
virtual void sound() const { // Виртуальная функция
std::cout << "Некоторый звук" << std::endl;
}
};

class Dog : public Animal {
public:
void sound() const override { // Переопределение
std::cout << "Гав-гав" << std::endl;
}
};

class Cat : public Animal {
public:
void sound() const override { // Переопределение
std::cout << "Мяу" << std::endl;
}
};

void makeSound(const Animal& animal) {
animal.sound(); // Динамическое определение, какой sound() вызывать
}

int main() {
Dog dog;
Cat cat;

makeSound(dog); // Вывод: Гав-гав
makeSound(cat); // Вывод: Мяу
return 0;
}


Ставь 👍 и забирай 📚 Базу знаний
Post #2235 539
🤔 Когда выбросили исключение из конструктора, о чем стоит помнить?

Если исключение выброшено из конструктора, деструктор объекта вызван не будет. Это требует ручного освобождения ресурсов, выделенных до момента выброса исключения.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2233 628
🤔 Какую операцию над вектором можно сделать, которая вызовет много копирования?

В C++ есть несколько операций над std::vector, которые могут привести к множественному копированию элементов. Рассмотрим основные из них.

🟠Изменение размера (`resize`)
Метод resize() может вызвать множественное копирование, если новый размер вектора превышает его текущую вместимость (capacity).
#include <iostream>
#include <vector>

struct Data {
int value;
Data(int v) : value(v) {}
Data(const Data& other) { // Конструктор копирования
value = other.value;
std::cout << "Copying Data: " << value << std::endl;
}
};

int main() {
std::vector<Data> vec(3, Data(10)); // Заполняем 3 элементами
std::cout << "Resizing..." << std::endl;
vec.resize(10, Data(20)); // Вектор расширяется, возможны копирования
}


🟠Вставка элемента (`insert` и `emplace`)
Если std::vector не имеет достаточного запаса (capacity), вставка нового элемента может привести к реаллокации и копированию всех элементов.
std::vector<int> vec = {1, 2, 3};
vec.insert(vec.begin(), 0); // Вставка в начало → все элементы сдвигаются


🟠Присваивание (`operator=`)
Если один std::vector присваивается другому, все элементы копируются.
std::vector<int> vec1 = {1, 2, 3, 4, 5};
std::vector<int> vec2;
vec2 = vec1; // Копирование всех элементов


🟠Возвращение `std::vector` из функции
Если возвращаем std::vector по значению, может произойти копирование (если не работает оптимизация RVO/NRVO или перемещение).
std::vector<int> createVector() {
std::vector<int> vec(100, 42);
return vec; // Может скопироваться, если нет RVO
}


Ставь 👍 и забирай 📚 Базу знаний
Post #2231 533
🤔 Unordered_set, unordered_map и set, map чем отличаются?

1. Set/Map: элементы хранятся в отсортированном порядке, используются бинарные деревья (обычно красно-чёрные).
2. Unordered_set/Unordered_map: элементы не сортируются, используют хеш-таблицы для быстрого доступа.
3. Set и Map медленнее на вставке/поиске (O(log N)), но позволяют итерировать в отсортированном порядке.
4. Unordered_set и Unordered_map быстрее для поиска (O(1) в среднем), но не поддерживают упорядоченный доступ.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2229 557
🤔 Какие знаешь сортировки?

Существует множество алгоритмов сортировки, каждый из которых имеет свои преимущества и недостатки в зависимости от условий использования. Рассмотрим основные из них.

🟠Сортировка пузырьком (Bubble Sort)
Простейший алгоритм, который многократно проходит по массиву, сравнивая соседние элементы и меняя их местами, если они стоят в неправильном порядке.
Сложность: O(n²) в худшем и среднем случаях, O(n) в лучшем случае (если массив уже отсортирован).
Когда использовать: Почти никогда, так как слишком медленный.
void bubbleSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
std::swap(arr[j], arr[j + 1]);
}
}
}
}


🟠Сортировка выбором (Selection Sort)
На каждом шаге ищется минимальный элемент и ставится в начало неотсортированной части массива.
Сложность: O(n²) всегда.
Когда использовать: Если важна простота реализации, но нужна немного лучшая производительность, чем у пузырьковой сортировки.
void selectionSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
int minIdx = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIdx]) {
minIdx = j;
}
}
std::swap(arr[i], arr[minIdx]);
}
}


🟠Сортировка вставками (Insertion Sort)
Берём один элемент и вставляем его в правильное место среди уже отсортированных элементов.
Сложность: O(n²) в худшем случае, O(n) в лучшем (если массив почти отсортирован).
Когда использовать: Для небольших массивов или почти отсортированных данных.
void insertionSort(int arr[], int n) {
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}


🟠Сортировка слиянием (Merge Sort)
Разделяем массив на две части, рекурсивно сортируем их и затем сливаем.
Сложность: O(n log n) всегда.
Когда использовать: Когда нужна стабильность и предсказуемая скорость работы.
void merge(int arr[], int l, int m, int r) {
int n1 = m - l + 1, n2 = r - m;
int L[n1], R[n2];
for (int i = 0; i < n1; i++) L[i] = arr[l + i];
for (int i = 0; i < n2; i++) R[i] = arr[m + 1 + i];

int i = 0, j = 0, k = l;
while (i < n1 && j < n2) arr[k++] = (L[i] < R[j]) ? L[i++] : R[j++];
while (i < n1) arr[k++] = L[i++];
while (j < n2) arr[k++] = R[j++];
}

void mergeSort(int arr[], int l, int r) {
if (l < r) {
int m = l + (r - l) / 2;
mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);
merge(arr, l, m, r);
}
}


Ставь 👍 и забирай 📚 Базу знаний
Post #2227 561
🤔 Если std::move() не перемещает данные, то что их перемещает?

Это просто явное преобразование объекта в rvalue-ссылку, что позволяет использовать семантику перемещения.
Реальное перемещение выполняется методами, поддерживающими rvalue-ссылки, например, конструктором перемещения или оператором присваивания.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2225 600
🤔 Для чего при перегрузке оператора присваивания возвращать ссылку?

При перегрузке оператора = в C++ принято возвращать ссылку на текущий объект (*this). Это позволяет цепочечное присваивание (a = b = c), а также улучшает производительность и удобство использования.

🚩Правильная сигнатура оператора `=`

class MyClass {
public:
MyClass& operator=(const MyClass& other) {
if (this != &other) { // Защита от самоприсваивания
// Копируем данные
}
return *this; // Возвращаем ссылку на текущий объект
}
};


🚩Что даёт возвращение ссылки (`T&`)?

Поддержка цепочки присваивания (a = b = c)
a = b = c;  // Интерпретируется как a = (b = c);


Пример
#include <iostream>

class MyClass {
public:
int value;
MyClass(int v) : value(v) {}

MyClass& operator=(const MyClass& other) {
if (this != &other) { // Защита от самоприсваивания
value = other.value;
}
return *this;
}
};

int main() {
MyClass a(1), b(2), c(3);
a = b = c; // Работает, потому что `b = c` возвращает ссылку на `b`
std::cout << "a: " << a.value << ", b: " << b.value << ", c: " << c.value << std::endl;
}


Вывод
a: 3, b: 3, c: 3


🟠Убираем лишнее копирование (повышаем производительность)
Если оператор = вернёт не ссылку, а объект, то произойдёт лишнее копирование.

Плохо (возвращаем объект, а не ссылку)
MyClass operator=(const MyClass& other) { // ⚠️ Ошибка: возвращаем объект
value = other.value;
return *this; // Здесь создаётся временный объект (лишняя копия!)
}


Хорошо (возвращаем T&)
MyClass& operator=(const MyClass& other) { // ✅ Возвращаем ссылку
value = other.value;
return *this;
}


🚩Поддержка `if (a = b)`

Если оператор = вернёт ссылку, мы можем использовать его в условии:
if ((a = b).value == 42) { // ОК
std::cout << "Присваивание выполнено, a == 42";
}


🚩Что если вернуть `void`?

Если оператор = вернёт void, цепочка a = b = c; не будет работать.
class MyClass {
public:
void operator=(const MyClass& other) { // ⚠️ Ошибка!
value = other.value;
}
};

int main() {
MyClass a, b, c;
a = b = c; // ❌ Ошибка: `b = c` возвращает void, а `a = void` не работает!
}


Ставь 👍 и забирай 📚 Базу знаний
Post #2223 655
🤔 О чем следует помнить при вызове исключений в конструкторе?

- Исключения из конструктора предотвращают создание объекта.
- Деструкторы членов класса или базового класса должны корректно освобождать ресурсы.
- Лучше избегать сложной логики, которая может выбрасывать исключения.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 👍 1
Post #2221 704
🤔 Сколько места занимают bool, char, short, int, long long, float, double?

- bool: 1 байт.
- char: 1 байт.
- short: 2 байта.
- int: 4 байта.
- long long: 8 байт.
- float: 4 байта.
- double: 8 байт.
Эти значения могут варьироваться в зависимости от платформы и компилятора.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2219 759
🤔 В чем разница vector и list?

Vector в C++ представляет собой динамический массив с последовательным размещением элементов в памяти, что обеспечивает быстрый доступ по индексу. List — это двусвязный список, где каждый элемент хранит указатели на соседние элементы, что обеспечивает быструю вставку и удаление. Вставка и удаление в vector могут быть медленными из-за необходимости сдвига элементов, тогда как в list такие операции происходят быстрее. Однако доступ по индексу в list требует обхода элементов, что делает его медленнее для случайного доступа.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 🔥 1
Post #2217 637
🤔 Чем отличаются STL контейнеры vector и array?

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

🟠Различия в управлении памятью
std::vector использует динамическую память, выделяемую в куче (heap). Его размер может изменяться во время выполнения.
std::array использует статическую память, выделяемую в стеке (stack) или в статической области памяти, и его размер фиксирован на этапе компиляции.
#include <vector>
#include <array>
#include <iostream>

int main() {
std::vector<int> vec = {1, 2, 3}; // Размер может изменяться динамически
vec.push_back(4); // Добавляем новый элемент

std::array<int, 3> arr = {1, 2, 3}; // Размер фиксирован, нельзя добавить новый элемент

std::cout << "Vector size: " << vec.size() << std::endl; // Выведет 4
std::cout << "Array size: " << arr.size() << std::endl; // Выведет 3

return 0;
}


🟠Гибкость и изменение размера
std::vector позволяет изменять размер в процессе работы, автоматически выделяя новую память при необходимости.
std::array имеет фиксированный размер, который нельзя изменить после создания.
std::vector<int> v = {1, 2, 3};
v.push_back(4); // Увеличиваем размер

std::array<int, 3> a = {1, 2, 3};
// a.push_back(4); // Ошибка! У std::array нет метода push_back


🟠Производительность
std::array работает быстрее, так как все данные хранятся в непрерывном участке памяти и нет затрат на динамическое выделение.
std::vector может требовать дополнительное время при изменении размера, так как может потребоваться новое выделение памяти и копирование элементов.
#include <vector>
#include <array>
#include <chrono>
#include <iostream>

int main() {
constexpr int N = 1'000'000;

std::vector<int> vec(N, 1); // Динамический массив
std::array<int, N> arr{}; // Статический массив

auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < N; ++i) vec[i] += 1;
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Vector time: "
<< std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()
<< " us" << std::endl;

start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < N; ++i) arr[i] += 1;
end = std::chrono::high_resolution_clock::now();
std::cout << "Array time: "
<< std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()
<< " us" << std::endl;

return 0;
}


🟠Совместимость с C-API
std::array хранит данные как обычный C-массив, поэтому можно легко передавать его в функции, ожидающие int*.
std::vector использует динамическую память, но можно получить указатель на внутренний буфер с помощью data().
void processArray(int* arr, size_t size) {
for (size_t i = 0; i < size; ++i) {
std::cout << arr[i] << " ";
}
}

int main() {
std::array<int, 3> arr = {1, 2, 3};
std::vector<int> vec = {4, 5, 6};

processArray(arr.data(), arr.size()); // std::array можно передавать в C-функции
processArray(vec.data(), vec.size()); // std::vector тоже можно передавать

return 0;
}


🟠Итераторы и стандартные алгоритмы
Оба контейнера поддерживают итераторы и совместимы со стандартными алгоритмами из #include <algorithm>
#include <iostream>
#include <vector>
#include <array>
#include <algorithm>

int main() {
std::vector<int> vec = {3, 1, 4, 1, 5};
std::array<int, 5> arr = {3, 1, 4, 1, 5};

std::sort(vec.begin(), vec.end());
std::sort(arr.begin(), arr.end());

for (int n : vec) std::cout << n << " "; // 1 1 3 4 5
std::cout << std::endl;
for (int n : arr) std::cout << n << " "; // 1 1 3 4 5

return 0;
}


Ставь 👍 и забирай 📚 Базу знаний
Post #2215 597
🤔 Как удалить элемент из начала vector, если порядок не важен?

Вместо сдвига всех элементов (O(n)), можно:
1. Переместить последний элемент на место первого (O(1)).
2. Удалить последний элемент (pop_back()).
Это нарушит порядок, но значительно ускорит удаление.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 👍 2
  • 🔥 2
Post #2213 785
🤔 В чем разница vector и list?

Являются контейнерами из стандартной библиотеки, но они значительно различаются по своей структуре и оптимальности использования в разных ситуациях.

🚩std::vector

Это динамический массив, который обеспечивает быстрый доступ к элементам по индексу. Вектор хранит свои элементы в непрерывном блоке памяти, что делает возможным обращение к элементам за константное время O(1). Однако, такое хранение делает вставку и удаление элементов в начало или середину вектора менее эффективными (в среднем O(n), где n — количество элементов), так как может потребоваться перемещение большого количества элементов для освобождения места или заполнения пробела.
#include <vector>
#include <iostream>

int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
vec.push_back(6); // Добавление элемента в конец вектора
std::cout << "Элемент по индексу 3: " << vec[3] << std::endl; // Быстрый доступ к элементам

return 0;
}


🚩std::list

В отличие от std::vector, представляет собой двусвязный список. Каждый элемент содержит данные и указатели на предыдущий и следующий элементы в списке. Это обеспечивает быструю вставку и удаление элементов в любом месте списка, так как требуется только изменение нескольких указателей (O(1)), независимо от размера списка. Однако, доступ к элементам в std::list осуществляется за линейное время O(n), так как для доступа к элементу нужно пройти через указатели от начала или конца списка.
#include <list>
#include <iostream>

int main() {
std::list<int> lst = {1, 2, 3, 4, 5};
lst.push_front(0); // Быстрая вставка в начало списка
lst.push_back(6); // Быстрая вставка в конец списка

for (auto it = lst.begin(); it != lst.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;

return 0;
}


Ставь 👍 и забирай 📚 Базу знаний
Post #2211 792
🤔 Знаешь правило 5ти и правило 0?

Правило пяти в C++ означает, что если класс определяет один (или более) из следующих специальных членов функций, то ему, скорее всего, необходимо определить и все пять: деструктор, конструктор копирования, оператор копирования, конструктор перемещения и оператор перемещения. Правило нуля говорит, что классы должны использовать существующие классы управления ресурсами, такие как smart pointers, которые обеспечивают автоматическое управление ресурсами, избегая необходимости явного определения этих функций.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 🔥 1
Post #2208 810
🤔 Какой обычно используется тип для счетчиков (count)?

Наиболее часто используется size_t или std::atomic<size_t> для потокобезопасности.
- size_t – стандартный тип беззнакового целого, который оптимизирован для хранения размеров контейнеров.
- std::atomic<size_t> – используется в многопоточной среде для атомарного увеличения/уменьшения счетчика.
- В некоторых случаях используют uint32_t или uint64_t, если важно ограничение по размеру.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2205 869
🤔 Как устроена хеш-таблица в unordered_map?

В unordered_map используется хеш-таблица с цепочечной адресацией:
- Данные распределяются по бакетам (ячейкам), определяемым хеш-функцией.
- Если два разных ключа попадают в один бакет (коллизия), элементы связываются в список.
- При большом количестве элементов происходит рехеширование – увеличение количества бакетов и перераспределение данных.
Эта структура обеспечивает быстрые операции поиска, вставки и удаления в среднем за O(1).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2201 780
🤔 В чем особенность выброса исключений в конструкторе и деструкторе?

Если исключение выбрасывается в конструкторе, объект считается несозданным, и деструктор для него не вызывается. Если исключение выбрасывается в деструкторе, оно может привести к завершению программы, если другое исключение уже активно.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2200 832
🤔 Какое преимущество даст строка построенная на list?

Обычно строки (std::string) хранятся в непрерывном буфере (массиве символов), что даёт быстрый доступ по индексу и эффективную работу с кешем. Но если строка будет реализована на связанном списке (std::list<char>), это изменит её свойства.

🚩Когда `std::list<char>` лучше `std::string`?

🟠Если часто вставляем/удаляем символы в середину
Например, текстовый редактор: вставка символов в произвольное место без сдвига массива. В std::string это O(n) (нужно двигать символы), в std::list<char> – O(1) (добавление элемента в связный список).

🟠Если изменяем строку в реальном времени
Например, парсер текста, который часто редактирует строку на лету.

🟠Если работаем с очень длинными строками
std::list<char> может быть полезен, если динамическое изменение памяти важнее экономии места.

🚩Когда `std::string` лучше?

🟠Если нужен быстрый доступ по индексу (`O(1)`)
В std::list<char> доступ к str[1000] требует прохода 1000 элементов (O(n)), а в std::string – просто O(1).

🟠Если важна компактность памяти
std::list<char> расходует дополнительные 8–16 байт на каждый символ (из-за указателей).
std::string использует непрерывную память, что намного эффективнее.

🟠Если нужно быстро копировать и перемещать строки
std::string использует small string optimization (SSO), поэтому маленькие строки хранятся прямо внутри объекта (без динамического выделения памяти).

Ставь 👍 и забирай 📚 Базу знаний
Post #2198 699
🤔 Что такое back_inserter, зачем он нужен?

Это адаптер итератора, который используется для вставки элементов в конец контейнера при помощи push_back(). Он упрощает работу с алгоритмами стандартной библиотеки (std::copy, std::generate, std::transform), позволяя динамически расширять контейнер.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Older posts →
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →