std::vector
из стандартной библиотеки C++ предоставляет динамический массив, который может изменять свой размер во время выполнения и автоматически управляет памятью. Ниже представлены основные способы использования std::vector
и его методов.Объявление и инициализация:
#include <vector>
std::vector<int> vec; // Пустой вектор целых чисел
std::vector<int> vec2 = {1, 2, 3}; // Инициализация с помощью списка инициализации
Добавление элементов:
vec.push_back(10); // Добавляет элемент в конец вектора
vec.emplace_back(20); // Конструирует и добавляет элемент в конец (более эффективно)
Доступ к элементам:
int first = vec[0]; // Доступ без проверки границ
int second = vec.at(1); // Доступ с проверкой границ
Размер и емкость:
size_t size = vec.size(); // Текущее количество элементов
size_t capacity = vec.capacity(); // Количество элементов, для которых зарезервирована память
vec.reserve(100); // Резервирует память для 100 элементов
Итерация по элементам:
for (size_t i = 0; i < vec.size(); ++i) {
// Доступ по индексу
}
for (const auto& value : vec) {
// Диапазонный цикл для доступа к элементам
}
Вставка и удаление элементов:
vec.insert(vec.begin() + 1, 15); // Вставляет 15 на вторую позицию
vec.erase(vec.begin()); // Удаляет первый элемент
Очистка и проверка на пустоту:
vec.clear(); // Удаляет все элементы из вектора
bool isEmpty = vec.empty(); // Проверяет, пуст ли вектор
std::vector
является мощным и гибким контейнером, который упрощает работу с динамическими массивами, предоставляя множество методов для управления содержимым и оптимизации производительности.Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Если в C++ исключение не перехвачено ни одним блоком
catch
, вызывается функция std::terminate()
, которая по умолчанию завершает программу. Это может привести к неожиданной остановке работы приложения без корректного освобождения ресурсов или выполнения завершающих действий.Например:
#include <iostream>
void func() {
throw std::runtime_error("Ошибка"); // Выбрасывает исключение
}
int main() {
func(); // Вызов функции, которая выбрасывает исключение
std::cout << "Завершение программы" << std::endl;
return 0;
}
В этом коде функция
func()
генерирует исключение std::runtime_error
, но отсутствует блок try-catch
для его обработки. В результате программа вызывает std::terminate()
и аварийно завершается, не выводя "Завершение программы".Чтобы избежать этого, необходимо перехватить исключение:
int main() {
try {
func(); // Попытка вызвать функцию, которая может бросить исключение
} catch (const std::exception& e) {
std::cerr << "Перехвачено исключение: " << e.what() << std::endl;
}
std::cout << "Завершение программы" << std::endl;
return 0;
}
Теперь исключение перехватывается, выводится сообщение об ошибке, и программа корректно продолжает работу, выводя "Завершение программы".
Если исключение не перехвачено, это может привести к утечкам ресурсов, некорректному состоянию данных и затруднить отладку. Поэтому важно обеспечить соответствующую обработку возможных исключений в критических местах кода.
Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
Паттерн "Абстрактная Фабрика" предоставляет интерфейс для создания семейств взаимосвязанных объектов без указания их конкретных классов. Это упрощает замену семейств продуктов и обеспечивает их совместимость.
Пример:
Абстрактные интерфейсы продуктов и фабрики:
// Абстрактный продукт
class Button {
public:
virtual void render() = 0;
};
// Абстрактная фабрика
class GUIFactory {
public:
virtual Button* createButton() = 0;
};
Конкретная реализация для Windows:
// Конкретный продукт для Windows
class WindowsButton : public Button {
public:
void render() override {
// Отображение кнопки в стиле Windows
}
};
// Конкретная фабрика для Windows
class WindowsFactory : public GUIFactory {
public:
Button* createButton() override {
return new WindowsButton();
}
};
Использование фабрики:
int main() {
GUIFactory* factory = new WindowsFactory();
Button* btn = factory->createButton();
btn->render();
// Очистка ресурсов
delete btn;
delete factory;
return 0;
}
Паттерн "Абстрактная Фабрика" позволяет создавать объекты, не связываясь с конкретными классами, что облегчает поддержку и расширение кода.
Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥1
Принцип DRY (Don't Repeat Yourself) — один из ключевых принципов разработки программного обеспечения, который призывает избегать дублирования кода или логики. Каждый фрагмент знаний должен иметь единственное, непротиворечивое и авторитетное представление в системе.
Преимущества применения DRY:
- Снижение количества ошибок: Изменения в одном месте отражаются во всех частях системы, использующих этот код.
- Упрощение сопровождения: Легче обновлять и изменять код, когда логика сосредоточена в одном месте.
- Повышение читаемости: Код становится более чистым и понятным.
Пример нарушения DRY:
int calculateArea(int width, int height) {
return width * height;
}
int calculateRectangleArea(int width, int height) {
return width * height;
}
Здесь две функции выполняют одинаковые операции, что приводит к дублированию.
Применение принципа DRY:
int calculateArea(int width, int height) {
return width * height;
}
Теперь имеется одна функция для расчета площади, которую можно использовать во всех случаях, избегая повторений.
Принцип DRY способствует созданию более устойчивого и качественного кода, облегчая его развитие и поддержку в долгосрочной перспективе.
Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Управление временем выполнения программы в C++ включает измерение времени исполнения, оптимизацию кода и контроль длительности операций.
Измерение времени выполнения:
#include <chrono>
auto start = std::chrono::high_resolution_clock::now(); // Начало отсчёта
// Код для измерения времени выполнения
auto end = std::chrono::high_resolution_clock::now(); // Конец отсчёта
std::chrono::duration<double> duration = end - start; // Вычисление продолжительности
std::cout << "Время выполнения: " << duration.count() << " секунд\n";
Оптимизация кода:
- Эффективные алгоритмы и структуры данных: Использование подходящих алгоритмов снижает время выполнения.
- Профилирование: Инструменты, такие как gprof, помогают выявить узкие места.
- Оптимизации компилятора: Флаги
-O2
, -O3
для GCC улучшают производительность.Параллельное выполнение:
Использование потоков (std::thread) или асинхронных задач (std::async) для распараллеливания работы.
Установка таймаутов:
#include <future>
std::future<void> result = std::async(std::launch::async, []() {
// Длительная операция
});
if (result.wait_for(std::chrono::seconds(5)) == std::future_status::timeout) {
// Обработка таймаута
}
Управление временем выполнения позволяет повысить эффективность программы и обеспечить своевременное выполнение задач.
Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
C++ является расширением языка C и предлагает дополнительные возможности, такие как поддержка объектно-ориентированного программирования (ООП), обобщенное программирование, стандартная библиотека шаблонов (STL) и другие современные особенности.
Основные отличия между C++ и C:
- Объектно-ориентированное программирование: C++ поддерживает классы, наследование, полиморфизм и инкапсуляцию, тогда как C не предоставляет этих возможностей.
- Шаблоны: C++ вводит шаблоны функций и классов для обобщенного программирования, что позволяет создавать универсальные и повторно используемые компоненты.
- Обработка исключений: В C++ имеется механизм обработки исключений (try, catch, throw), который отсутствует в C.
- Стандартная библиотека: C++ включает STL, предоставляющую контейнеры, алгоритмы и итераторы, упрощающие разработку.
- Пространства имен: C++ использует
namespace
для организации кода и предотвращения конфликтов имен.- Перегрузка функций и операторов: В C++ возможно создавать несколько функций с одинаковым именем, но разными параметрами, а также перегружать операторы для пользовательских типов.
Пример класса в C++:
class Point {
public:
Point(int x, int y) : x_(x), y_(y) {} // Конструктор
int getX() const { return x_; } // Получение координаты X
int getY() const { return y_; } // Получение координаты Y
private:
int x_;
int y_;
};
В C такой функционал необходимо реализовывать вручную, используя структуры и функции без поддержки ООП. C++ обеспечивает более высокий уровень абстракции и удобства для разработчиков, позволяя писать более чистый и модульный код.
Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
В C++ существует несколько подходов к структурированию кода, которые помогают создавать поддерживаемые и масштабируемые приложения.
1. Процедурное программирование:
Основано на разделении программы на функции.
int add(int a, int b) {
return a + b; // Функция для сложения двух чисел
}
2. Объектно-ориентированное программирование (ООП):
Использует классы и объекты для моделирования сущностей и их взаимодействий.
class Animal {
public:
void speak() {
// Реализация метода speak
}
};
3. Шаблонное программирование:
Позволяет создавать обобщенный код с помощью шаблонов.
template<typename T>
T max(T a, T b) {
return (a > b) ? a : b; // Функция для получения максимального значения
}
4. Модульность:
Разделение кода на файлы заголовков (.h) и реализации (.cpp) для улучшения организации.
5. Использование пространств имен:
Помогает избежать конфликтов имен и организовать код.
namespace Math {
int multiply(int a, int b) {
return a * b; // Функция умножения в пространстве имен Math
}
}
6. Функциональное программирование:
Использование ламбда-функций и стандартных алгоритмов.
#include <algorithm>
#include <vector>
std::vector<int> vec = {1, 2, 3, 4};
std::for_each(vec.begin(), vec.end(), [](int& n) {
n *= 2; // Удвоение каждого элемента
});
Комбинируя эти подходы, можно создавать эффективный и чистый код, облегчающий сопровождение и развитие проектов.
Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤2🔥1
std::deque
(double-ended queue) в C++ — это контейнер из стандартной библиотеки, который представляет собой двунаправленную очередь с возможностью эффективного добавления и удаления элементов.Основные особенности std::deque:
- Быстрый доступ к элементам: обеспечивает константное время доступа по индексу, аналогично
std::vector
.- Эффективное добавление и удаление: операции вставки и удаления в начало и конец выполняются быстро.
- Гибкость: подходит для случаев, когда требуется часто добавлять или удалять элементы.
Когда использовать std::deque:
- При необходимости частых операций вставки/удаления в начало и конец.
- Когда требуется быстрый доступ к элементам по индексу.
- Если нужно комбинировать преимущества
std::vector
и std::list
.Пример использования std::deque:
#include <deque>
#include <iostream>
int main() {
std::deque<int> dq;
dq.push_back(1); // Добавление в конец
dq.push_front(2); // Добавление в начало
dq[0] = 3; // Изменение элемента по индексу
// Итерация по элементам
for (const auto& elem : dq) {
std::cout << elem << " ";
}
// Вывод: 3 1
dq.pop_back(); // Удаление с конца
dq.pop_front(); // Удаление с начала
std::cout << "\nРазмер deque: " << dq.size(); // Вывод размера
return 0;
}
Основные методы std::deque:
-
push_back(value)
и push_front(value)
— добавление элементов в конец и начало.-
pop_back()
и pop_front()
— удаление элементов с конца и начала.-
operator[](index)
и at(index)
— доступ к элементу по индексу.-
size()
— получение количества элементов.-
empty()
— проверка на пустоту контейнера.-
clear()
— удаление всех элементов.std::deque
следует использовать, когда требуется эффективное добавление и удаление элементов вместе с быстрым доступом по индексу. Это делает его полезным в ситуациях, где нужны гибкость и производительность при работе с последовательностями данных.Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Кеш процессора — это небольшая, но очень быстрая память, расположенная непосредственно внутри или рядом с ядрами CPU. Он предназначен для уменьшения времени доступа к данным и инструкциям, которые часто используются, что значительно повышает производительность программ.
Уровни кеша:
- L1 кеш: Самый быстрый и небольшой по объему, разделен на инструкционный и кеш данных.
- L2 кеш: Больше по размеру, медленнее L1, может быть общий для пары ядер.
- L3 кеш: Еще больше и медленнее, обычно общий для всех ядер процессора.
Влияние на производительность:
- Локальность данных: Структурирование кода так, чтобы часто используемые данные были расположены в памяти близко друг к другу, улучшает эффективность кеша.
- Паттерны доступа: Последовательный доступ к памяти более эффективен для кеширования, чем случайный.
Пример кода:
// Последовательный доступ к элементам массива (эффективно для кеша)
int array[1000];
for (int i = 0; i < 1000; ++i) {
array[i] = i;
}
// Случайный доступ к элементам массива (менее эффективно для кеша)
int array[1000];
for (int i = 0; i < 1000; ++i) {
int index = rand() % 1000;
array[index] = index;
}
Оптимизация программы с учетом особенностей кеша может существенно сократить время выполнения за счет уменьшения количества обращений к медленной оперативной памяти. Понимание работы кеша помогает писать более эффективный код, особенно в вычислительно интенсивных приложениях.
Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Умные указатели в C++ автоматизируют управление динамической памятью, предотвращая утечки и обеспечивая безопасное владение ресурсами. Основные типы:
1. std::unique_ptr – обеспечивает уникальное владение объектом, не допускает копирования, но поддерживает перемещение.
std::unique_ptr<int> ptr = std::make_unique<int>(10);
2. std::shared_ptr – поддерживает совместное владение объектом с подсчетом ссылок. Объект удаляется, когда счетчик достигает нуля.
std::shared_ptr<int> ptr1 = std::make_shared<int>(20);
std::shared_ptr<int> ptr2 = ptr1;
3. std::weak_ptr – предоставляет небелую ссылку на объект, управляемый
shared_ptr
, предотвращая циклические зависимости.
std::weak_ptr<int> weakPtr = ptr1;
Использование умных указателей способствует более безопасному и эффективному управлению ресурсами в C++.
Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Рекурсия в C++ представляет собой метод, при котором функция вызывает сама себя для решения подзадач, приближаясь к базовому случаю.
Пример рекурсии: вычисление факториала
int factorial(int n) {
if (n <= 1) return 1;
else return n * factorial(n - 1);
}
Основные компоненты рекурсии:
1. Базовый случай – условие прекращения рекурсии, предотвращающее бесконечные вызовы.
2. Рекурсивный шаг – функция вызывает сама себя с изменёнными параметрами, приближаясь к базовому случаю.
Преимущества:
- Упрощение решения сложных задач, таких как обход деревьев или графов.
Недостатки:
- Возможное переполнение стека при глубокой рекурсии.
- Более высокий расход памяти по сравнению с итеративными решениями.
Рекурсия является инструментом, позволяющим эффективно решать разнообразные задачи при правильном использовании.
Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Разыменование указателя позволяет получить или изменить значение по адресу, на который он указывает. Используется оператор *.
Пример разыменования:
int value = 5;
int* ptr = &value;
int deref = *ptr; // deref равно 5
*ptr = 10; // value теперь равно 10
Для доступа к членам объекта через указатель применяется оператор ->.
Пример с объектом:
struct Point {
int x;
int y;
};
Point p = {1, 2};
Point* ptr = &p;
int xValue = ptr->x; // xValue равно 1
Важно:
Перед разыменованием указателя следует проверить, что он не равен
nullptr
, чтобы избежать неопределённого поведения.
if (ptr != nullptr) {
// Безопасное разыменование
int val = *ptr;
}
Разыменование указателей является фундаментальной операцией в C++, позволяющей эффективно управлять памятью и работать с динамическими структурами данных.
Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Виртуальные функции в C++ обеспечивают полиморфизм, позволяя производным классам переопределять методы базового класса. Это достигается с помощью ключевого слова
virtual
.Пример использования:
#include <iostream>
class Animal {
public:
virtual void speak() const {
std::cout << "Animal sound\n";
}
virtual ~Animal() = default;
};
class Dog : public Animal {
public:
void speak() const override {
std::cout << "Woof!\n";
}
};
void makeSpeak(const Animal& animal) {
animal.speak();
}
int main() {
Animal a;
Dog d;
makeSpeak(a); // Вывод: Animal sound
makeSpeak(d); // Вывод: Woof!
return 0;
}
Ключевые моменты:
1. Ключевое слово virtual – объявляет функцию как виртуальную в базовом классе.
2. Переопределение – производные классы могут реализовать свою версию виртуальной функции с использованием
override
для явного указания.3. Деструкторы – виртуальные деструкторы гарантируют корректное уничтожение объектов при удалении через указатель на базовый класс.
4. Полиморфизм времени выполнения – вызов виртуальной функции определяется во время выполнения, позволяя работать с объектами различных типов через единый интерфейс.
Виртуальные функции являются фундаментом объектно-ориентированного программирования в C++, обеспечивая гибкость и расширяемость кода.
Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Основные компоненты STL в C++ обеспечивают инструменты для эффективного программирования.
1. Контейнеры
Хранят и организуют данные. Основные типы:
- vector – динамический массив.
::vector<int> nums = {1, 2, 3};
- map – ассоциативный массив с ключами и значениями.
std::map<std::string, int> age = {{"Alice", 30}, {"Bob", 25}};
- list – двусвязный список.
2. Алгоритмы
Предоставляют общие операции над данными, такие как сортировка, поиск и трансформация.
- std::sort – сортирует элементы.
::sort(nums.begin(), nums.end());
- std::find – ищет элемент.
auto it = std::find(nums.begin(), nums.end(), 2);
3. Итераторы
Обеспечивают универсальный доступ к элементам контейнеров, аналог указателей.
- Пример использования итератора:
for(auto it = nums.begin(); it != nums.end(); ++it) {
std::cout << *it << " ";
}
4. Функторы
Объекты, которые можно вызывать как функции, используемые в алгоритмах.
struct Compare {
bool operator()(int a, int b) { return a < b; }
};
std::sort(nums.begin(), nums.end(), Compare());
5. Аллокаторы
Управляют памятью для контейнеров, позволяют настраивать стратегии выделения.
Использование компонентов STL повышает эффективность разработки и качество кода в C++.
Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
std::unique_ptr
и std::shared_ptr
являются умными указателями в C++, обеспечивающими автоматическое управление динамической памятью, но отличаются механизмами владения и управления ресурсами.std::unique_ptr
Обеспечивает уникальное владение объектом. Нельзя копировать, но можно перемещать.
std::unique_ptr<int> ptr1 = std::make_unique<int>(10);
// std::unique_ptr<int> ptr2 = ptr1; // Ошибка
std::unique_ptr<int> ptr2 = std::move(ptr1); // Перемещение
Преимущества:
- Легковесность.
- Явное владение ресурсом.
- Подходит для строгого владения объектом.
std::shared_ptr
Позволяет совместное владение объектом с подсчетом ссылок. Объект удаляется, когда последний
shared_ptr
уничтожается.
std::shared_ptr<int> ptr1 = std::make_shared<int>(20);
std::shared_ptr<int> ptr2 = ptr1; // Совместное владение
Преимущества:
- Удобно для разделяемых ресурсов.
- Автоматическое управление временем жизни объекта.
Отличия:
-
unique_ptr
имеет эксклюзивное владение и меньше накладных расходов.-
shared_ptr
поддерживает совместное владение, что требует дополнительного памяти для хранения счетчика ссылок.Выбор между ними зависит от требований к владению ресурсом и необходимости совместного использования объекта.
Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Арифметика указателей позволяет выполнять операции с адресами памяти, учитывая размер типа данных.
Основные операции:
1. Инкремент (++) – увеличивает адрес на размер типа данных.
int arr[5] = {1, 2, 3, 4, 5};
int* ptr = arr;
ptr++; // указывает на arr[1]
2. Декремент (--) – уменьшает адрес на размер типа данных.
ptr--; // возвращается к arr[0]
3. Сложение (+) – смещает указатель на указанное количество элементов.
ptr = ptr + 2; // указывает на arr[2]
4. Вычитание (-) – смещает указатель назад.
ptr = ptr - 1; // указывает на arr[1]
5. Разность указателей – вычисляет количество элементов между двумя указателями.
int* start = arr;
int* end = arr + 5;
ptrdiff_t diff = end - start; // diff = 5
Особенности:
- Арифметика указателей обычно используется с массивами.
- Указатели должны принадлежать одному массиву для корректной разности.
- Операции учитывают размер типа данных, на который указывает указатель.
Арифметика указателей упрощает навигацию по массивам и работу с динамическими структурами данных в C++.
Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
Условные переменные (std::condition_variable) позволяют потокам ждать наступления определённого условия, обеспечивая эффективную синхронизацию.
Основные компоненты:
1. std::mutex – обеспечивает взаимное исключение при доступе к общим данным.
2. std::condition_variable – позволяет потокам блокироваться и ожидать уведомления.
3. Условия – логические выражения, определяющие, когда поток может продолжить выполнение.
Пример использования:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void worker() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; });
std::cout << "Работа началась\n";
}
int main() {
std::thread t(worker);
// Подготовка
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one(); // Уведомление потока
t.join();
return 0;
}
Ключевые моменты:
- Блокировка mutex перед ожиданием.
- wait автоматически разблокирует mutex и блокирует поток до уведомления.
- notify_one или notify_all для пробуждения ожидающих потоков.
- Использование лямбда-функции для проверки условия, предотвращая ложные пробуждения.
Условные переменные эффективно управляют синхронизацией потоков, позволяя ожидать специфических событий без постоянной проверки условий.
Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
std::list
представляет собой двусвязный список, входящий в STL, который обеспечивает эффективные операции вставки и удаления элементов в любом месте списка.Основные характеристики:
- Двунаправленность: Каждый элемент хранит ссылки на предыдущий и следующий элементы, что позволяет легко перемещаться в обоих направлениях.
- Динамическое управление памятью: Элементы не расположены в непрерывной области памяти, что позволяет быстро вставлять и удалять элементы без перераспределения.
- Итераторы: Поддерживает двунаправленные итераторы для перебора элементов.
- Операции: Предоставляет операции вставки, удаления, слияния и разворота списков с линейной сложностью времени.
Пример использования:
#include <iostream>
#include <list>
int main() {
std::list<int> numbers = {1, 2, 3, 4, 5};
// Вставка элемента
numbers.insert(++numbers.begin(), 10);
// Удаление элемента
numbers.erase(--numbers.end());
// Перебор элементов
for(const auto& num : numbers) {
std::cout << num << " ";
}
// Вывод: 1 10 2 3 4
return 0;
}
Преимущества:
- Быстрая вставка и удаление элементов в любом месте списка.
- Нет необходимости в перемещении элементов при изменении размера.
Недостатки:
- Более высокий расход памяти из-за хранения дополнительных указателей.
- Медленный доступ к элементам по индексу по сравнению с
std::vector
.std::list
идеально подходит для сценариев, где необходима частая модификация структуры данных, но не требуется быстрый доступ к элементам по индексу.Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Stack unwinding – процесс очистки стека вызовов при возникновении исключения. Когда исключение выбрасывается, система начинает разрушать объекты в обратном порядке создания, вызывая их деструкторы до тех пор, пока исключение не будет поймано.
Пример:
#include <iostream>
#include <exception>
class Resource {
public:
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource released\n"; }
};
void func() {
Resource res;
throw std::runtime_error("Error occurred");
}
int main() {
try {
func();
} catch(const std::exception& e) {
std::cout << "Caught exception: " << e.what() << "\n";
}
return 0;
}
Ключевые моменты:
- Автоматическое разрушение объектов: Все локальные объекты в стеке уничтожаются, предотвращая утечки ресурсов.
- Гарантия вызова деструкторов: Даже при возникновении исключений, деструкторы вызываются для корректного освобождения ресурсов.
- Безопасное управление ресурсами: Stack unwinding поддерживает идиому RAII, обеспечивая надежное управление жизненным циклом объектов.
Принцип stack unwinding обеспечивает безопасную обработку исключений, автоматически управляя освобождением ресурсов и поддерживая стабильность программы.
Ставь 👍, если было полезно!
Еще больше ответов для подготовки к собеседованиям на сайте 👈
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5