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

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

@easy_c_plus

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

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

Showing posts older than #2167 · Back to latest

Older Posts 20 shown
Post #2165 915
🤔 Как работает new?

Оператор new в C++ выделяет память из кучи (heap) для хранения объекта и возвращает указатель на эту область памяти. Помимо выделения памяти, new также вызывает конструктор объекта, если он определен. В случае, если не хватает памяти для выделения, new выбрасывает исключение std::bad_alloc. Для освобождения памяти, выделенной через new, необходимо использовать оператор delete.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 👍 2
  • 🔥 1
  • 🤔 1
Post #2163 818
🤔 Как убедиться что нет багов в коде?

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

🟠Внимательная проверка кода (Code Review)
Перед тем как запускать код, полезно просмотреть его вручную или с коллегами. Это помогает:
Найти логические ошибки
Улучшить читаемость кода
Проверить соответствие код-стайлу
// Опытный разработчик может заметить, что деление на 0 возможно
int divide(int a, int b) {
return a / b; // Потенциальная ошибка, если b == 0
}


Лучше добавить проверку
int divide(int a, int b) {
if (b == 0) {
throw std::runtime_error("Деление на ноль!");
}
return a / b;
}


🟠Юнит-тестирование (Unit Testing)
Тесты проверяют каждую функцию отдельно, чтобы убедиться, что она работает правильно. В C++ используют Google Test (gtest), Catch2 или Boost.Test.
#include <gtest/gtest.h>
#include "math_utils.h"

TEST(DivideFunctionTest, HandlesZeroDenominator) {
EXPECT_THROW(divide(10, 0), std::runtime_error);
}

TEST(DivideFunctionTest, NormalCase) {
EXPECT_EQ(divide(10, 2), 5);
}


🟠Отладка (Debugging)
Если код работает не так, как ожидалось, используют отладчики (GDB, LLDB, Visual Studio Debugger). Они позволяют пошагово исполнять код и смотреть, где происходит ошибка.
g++ -g main.cpp -o program  # Компилируем с отладочной информацией
gdb ./program # Запускаем отладчик


🟠Статический анализ кода
Используются специальные инструменты, которые автоматически ищут ошибки в коде:
Clang-Tidy
Cppcheck
SonarQube
cppcheck --enable=all my_code.cpp


🟠Динамический анализ (Sanitizers, Valgrind)
Эти инструменты помогают находить утечки памяти, переполнения буфера и другие ошибки.
g++ -fsanitize=address -g main.cpp -o program
./program # Если есть проблемы, они будут найдены


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

Ставь 👍 и забирай 📚 Базу знаний
Post #2162 726
🤔 Может ли быть проблема со вставкой ста элементов через push_back?

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

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2160 753
🤔 Какая сложность операции вставки у list?

Вставка в list имеет сложность O(1), так как требуется лишь обновление ссылок соседних узлов.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 👍 2
  • 🔥 1
Post #2159 811
🤔 Когда при вызове exception не зовутся деструкторы?

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

🟠Динамически выделенные объекты (`new`)
Если объект был создан с помощью new, но перед его удалением произошло исключение, деструктор не вызовется автоматически.
#include <iostream>

class A {
public:
A() { std::cout << "Конструктор A\n"; }
~A() { std::cout << "Деструктор A\n"; }
};

void func() {
A* obj = new A(); // Создаем объект динамически
throw std::runtime_error("Ошибка!"); // Исключение, объект утечет
delete obj; // Никогда не выполнится
}

int main() {
try {
func();
} catch (...) {
std::cout << "Исключение поймано\n";
}
}


Как исправить?
Использовать std::unique_ptr, который автоматически удаляет объект
#include <memory>

void func() {
std::unique_ptr<A> obj = std::make_unique<A>();
throw std::runtime_error("Ошибка!"); // Деструктор вызовется автоматически
}


🟠Локальные объекты, созданные `new` внутри `try`, но `catch` не обработал исключение
Если объект выделяется внутри try, но исключение не ловится в catch, программа завершится (std::terminate), и деструкторы не вызовутся.
void func() {
A* obj = new A();
throw std::runtime_error("Ошибка!"); // std::terminate()
delete obj;
}

int main() {
func(); // Исключение не ловится => программа аварийно завершится
}


Как исправить?
Нужно обрабатывать исключение на верхнем уровне
int main() {
try {
func();
} catch (...) {
std::cout << "Ошибка обработана\n";
}
}


🟠Исключение в конструкторе
Если исключение возникает в конструкторе, деструктор этого объекта не вызывается, но деструкторы уже созданных объектов будут вызваны.
class B {
public:
B() { std::cout << "Конструктор B\n"; }
~B() { std::cout << "Деструктор B\n"; }
};

class A {
public:
A() {
std::cout << "Конструктор A\n";
throw std::runtime_error("Ошибка в конструкторе A");
}
~A() { std::cout << "Деструктор A\n"; } // Никогда не вызовется!
};

int main() {
try {
B b;
A a; // Исключение в конструкторе A
} catch (...) {
std::cout << "Исключение обработано\n";
}
}


Вывод
Конструктор B
Конструктор A
Исключение обработано
Деструктор B


Как исправить?
- Использовать концепцию "умные указатели" (std::unique_ptr), чтобы ресурсы очищались автоматически.
- Переносить ресурсоемкие операции в отдельный метод (init()), который можно вызывать после конструктора.

🟠Исключение в деструкторе
Если деструктор объекта бросает исключение, это опасно, особенно если исключение возникло в момент обработки другого исключения.
class A {
public:
~A() {
std::cout << "Деструктор A\n";
throw std::runtime_error("Ошибка в деструкторе!"); // Ошибка
}
};

int main() {
try {
A a;
throw std::runtime_error("Основное исключение");
} catch (...) {
std::cout << "Исключение обработано\n";
}
}


Как исправить?
Деструкторы не должны выбрасывать исключения. Если исключение возможно, его следует перехватывать внутри деструктора
class A {
public:
~A() {
try {
throw std::runtime_error("Ошибка в деструкторе!");
} catch (...) {
std::cout << "Ошибка подавлена в деструкторе\n";
}
}
};


Ставь 👍 и забирай 📚 Базу знаний
Post #2157 714
🤔 Что будет если в функции помеченной как noexcept бросить исключение?

Если функция, помеченная как `noexcept`, выбросит исключение, программа вызовет `std::terminate()`, что приведёт к немедленному завершению выполнения. Это связано с тем, что `noexcept` гарантирует, что функция не выбросит исключений, и нарушение этого обещания считается критической ошибкой. Использование `noexcept` позволяет оптимизировать код, так как компилятор может делать определённые оптимизации, полагаясь на то, что исключения не будут выбрасываться. Следует избегать выбрасывания исключений в таких функциях.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 🔥 1
Post #2155 720
🤔 Расскажи про константные методы?

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

🚩Объявление и определение константного метода

Чтобы объявить метод как константный, нужно добавить ключевое слово const после списка параметров метода:
class MyClass {
public:
int getValue() const; // Константный метод
void setValue(int newValue); // Неконстантный метод

private:
int value;
};

// Определение константного метода
int MyClass::getValue() const {
return value; // Допустимо: метод не изменяет состояние объекта
}

// Определение неконстантного метода
void MyClass::setValue(int newValue) {
value = newValue; // Допустимо: метод изменяет состояние объекта
}


🚩Пример использования константных методов

#include <iostream>

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

int getValue() const { // Константный метод
return value;
}

void setValue(int newValue) { // Неконстантный метод
value = newValue;
}

private:
int value;
};

int main() {
MyClass obj(10);

std::cout << "Value: " << obj.getValue() << std::endl; // Вывод: 10

obj.setValue(20);
std::cout << "New value: " << obj.getValue() << std::endl; // Вывод: 20

// Константный объект
const MyClass constObj(30);
std::cout << "Const object value: " << constObj.getValue() << std::endl; // Вывод: 30

// constObj.setValue(40); // Ошибка: setValue не может быть вызван для константного объекта

return 0;
}


🚩Почему и когда использовать константные методы

🟠Гарантия неизменности
Константные методы гарантируют, что состояние объекта не будет изменено. Это полезно для методов, которые предназначены только для чтения данных объекта, например, методы доступа (геттеры).
🟠Безопасность и семантика
Константные методы помогают лучше понимать код и повышают его безопасность, так как ясно видно, какие методы могут изменять состояние объекта, а какие нет.
🟠Работа с константными объектами
Константные методы можно вызывать на константных объектах и через константные ссылки или указатели. Это позволяет использовать объекты в контексте, где важно гарантировать их неизменность.

🚩Вызов константных методов на константных объектах

const MyClass constObj(30);
constObj.getValue(); // Допустимо: getValue — константный метод
// constObj.setValue(40); // Ошибка: setValue — неконстантный метод


Ставь 👍 и забирай 📚 Базу знаний
  • 💊 1
Post #2154 771
🤔 Какая сложность поиска в set и unordered_set?

В set поиск имеет сложность O(log n), так как он реализован как сбалансированное бинарное дерево. В unordered_set сложность поиска в среднем O(1), но в худшем случае (при большом количестве коллизий) может достигать O(n).

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 👍 1
Post #2153 999
🤔 Сложность поиска в бинарных деревьях логарифмическая, всегда ли так?

Нет, сложность поиска в бинарных деревьях не всегда логарифмическая. Она зависит от структуры дерева. Хотя логарифмическая сложность \(O(\log N)\) считается идеальной, это справедливо только для сбалансированных бинарных деревьев. Давайте разберём, когда эта сложность сохраняется, а когда может увеличиваться.

🚩Идеальный случай: сбалансированное бинарное дерево

Если бинарное дерево поиска (Binary Search Tree, BST) сбалансировано, глубина дерева пропорциональна \( \log_2 N \), где \(N\) – количество узлов. В этом случае поиск, вставка и удаление элемента выполняются за \(O(\log N)\).

🚩Худший случай: несбалансированное дерево

Если дерево несбалансировано, то оно может выродиться в связный список, где каждый узел имеет только одного потомка (левого или правого).

🚩Как избежать вырождения дерева?

Чтобы поддерживать сложность операций \(O(\log N)\), используют сбалансированные бинарные деревья, такие как:
🟠AVL-деревья
Поддерживают балансировку после каждой операции вставки/удаления.
🟠Красно-чёрные деревья
Гарантируют, что глубина дерева остаётся \(O(\log N)\).
🟠B-деревья и B+ деревья
Используются для работы с большими объёмами данных, например, в базах данных.

Ставь 👍 и забирай 📚 Базу знаний
Post #2147 1.85K
🤔 Что знаешь про гарантии безопасности исключений?

Гарантии безопасности исключений (Exception Safety Guarantees) — это концепция, связанная с корректным поведением программы при возникновении исключений. Она определяет, насколько безопасно может завершиться выполнение функции или блока кода в случае выбрасывания исключения.

🚩Никаких гарантий (No Guarantee)

Этот уровень означает, что при возникновении исключения состояние программы может быть непредсказуемым. Объекты могут остаться в недопустимом состоянии, и поведение программы после выброса исключения неопределено.
void unsafeFunction(std::vector<int>& vec, int value) {
vec.push_back(value); // Если здесь выбросится исключение, состояние vec не определено
// ...
}


🚩Базовая гарантия (Basic Guarantee)

Этот уровень гарантирует, что не произойдёт утечек ресурсов или нарушений инвариантов объектов. После выброса исключения все объекты остаются в допустимом состоянии, однако состояние программы может быть частично изменено.
void safeFunction(std::vector<int>& vec, int value) {
try {
vec.push_back(value); // Если исключение, состояние vec остаётся корректным
} catch (...) {
// Обработка исключения
std::cerr << "Ошибка при добавлении элемента!" << std::endl;
}
}


🚩Сильная гарантия (Strong Guarantee)

Этот уровень гарантирует, что в случае возникновения исключения программа останется в исходном состоянии, как будто вызов функции никогда не происходил. Состояние откатывается до того, что было до вызова функции.
void addValue(std::vector<int>& vec, int value) {
std::vector<int> temp = vec; // Создаём копию
temp.push_back(value); // Работаем с копией
vec = temp; // Замена содержимого
}


🚩Гарантия отсутствия исключений (No-Throw Guarantee)

Этот уровень гарантирует, что функция никогда не выбрасывает исключений. Обычно применяется к ключевым операциям (например, деструкторам, перемещениям).
void safeSwap(std::vector<int>& a, std::vector<int>& b) noexcept {
a.swap(b); // std::vector::swap гарантирует отсутствие исключений
}


🚩Как достигаются гарантии безопасности исключений?

🟠RAII (Resource Acquisition Is Initialization)
Использование классов, где ресурсы освобождаются в деструкторах. Например, std::unique_ptr или std::lock_guard.

🟠Копирование перед изменением
Применение принципа работы с временными копиями (как в примере сильной гарантии).

🟠Использование стандартных контейнеров
Библиотека STL предоставляет базовую гарантию безопасности исключений для большинства операций.

Ставь 👍 и забирай 📚 Базу знаний
  • 👍 1
Post #2146 1.16K
🤔 Чем отличаются STL-контейнеры vector и array?

1. vector:
- Динамический массив, размер которого можно изменять.
- Управляет памятью автоматически.
- Подходит для сценариев, где размер данных неизвестен заранее.
2. array:
- Статический массив, размер которого фиксирован при создании.
- Не выделяет и не освобождает память динамически.
- Быстрее и эффективнее для небольших данных с фиксированным размером.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2145 1.05K
🤔 В чем разница map и unordered_map?

std::map и std::unordered_map – оба являются ассоциативными контейнерами в C++, но они устроены по-разному и имеют разные скорости работы.

🚩Как устроены контейнеры?

🟠`std::map` – красно-чёрное дерево (сбалансированное BST)
Все ключи хранятся упорядоченно.
Операции insert, erase, find выполняются за O(log n).
Упорядоченность важна, если требуется поиск в диапазоне (lower_bound, upper_bound).
#include <map>
#include <iostream>

int main() {
std::map<int, std::string> m;
m[2] = "B";
m[1] = "A";
m[3] = "C";

for (const auto& [key, value] : m) {
std::cout << key << " -> " << value << '\n';
}
}


Вывод (отсортирован!)
1 -> A  
2 -> B
3 -> C


🟠`std::unordered_map` – хеш-таблица
Нет сортировки, но поиск выполняется быстро – O(1) (в среднем).
Использует хеш-функцию, поэтому доступ к элементам зависит от хеширования.
Может быть медленным при коллизиях (O(n) в худшем случае).
#include <unordered_map>
#include <iostream>

int main() {
std::unordered_map<int, std::string> um;
um[2] = "B";
um[1] = "A";
um[3] = "C";

for (const auto& [key, value] : um) {
std::cout << key << " -> " << value << '\n';
}
}


Вывод (порядок непредсказуем!)
2 -> B  
1 -> A
3 -> C


🚩Когда использовать `map`, а когда `unordered_map`?

🟠Используйте `std::map`, если:
Нужно сортированное хранение ключей.
Требуется поиск по диапазону (lower_bound, upper_bound).
Хеш-функция для ключей сложна или недоступна (например, std::pair без кастомного хешера).

Ставь 👍 и забирай 📚 Базу знаний
Post #2144 734
🤔 Какая сложность операции вставки у list?

Вставка в list имеет сложность O(1), так как требуется лишь обновление ссылок соседних узлов.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
  • 👍 2
Post #2143 858
🤔 Для чего используется ключевое слово volatile?

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

🚩Основные применения


🟠Аппаратные регистры
Переменные, связанные с аппаратными устройствами, такими как порты ввода-вывода, часто меняются вне контроля программы. Использование volatile предотвращает оптимизации, которые могли бы кэшировать значение регистров.
volatile int* port = reinterpret_cast<int*>(0x400);
*port = 42; // Запись в аппаратный регистр


🟠Переменные, изменяемые прерываниями
В системах реального времени и встроенных системах значения переменных могут изменяться в обработчиках прерываний. volatile гарантирует, что каждое обращение к такой переменной будет действительным.
volatile bool interruptFlag = false;

void interruptHandler() {
interruptFlag = true; // Устанавливается в обработчике прерывания
}

void mainFunction() {
while (!interruptFlag) {
// Ожидание установки флага прерывания
}
// Обработка прерывания
}


🟠Многопоточные приложения
Переменные, которые могут изменяться из других потоков, также могут быть помечены как volatile. Однако следует отметить, что volatile не обеспечивает защиту от гонок данных и не заменяет средства синхронизации, такие как мьютексы или атомарные операции.
volatile bool stopThread = false;

void workerThread() {
while (!stopThread) {
// Выполнение работы
}
}

int main() {
std::thread t(workerThread);
// ...
stopThread = true; // Остановка рабочего потока
t.join();
return 0;
}


🚩Особенности и ограничения

🟠Оптимизация компиляции
volatile предотвращает оптимизации компилятором, которые могли бы удалить или кэшировать доступы к переменной.

🟠Не потокобезопасность

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

🟠Совместное использование с `const`
Переменные могут быть как const volatile, если они не должны изменяться программой, но могут изменяться внешними факторами.

🚩Пример: Аппаратные регистры

#include <iostream>

volatile int* hardwareRegister = reinterpret_cast<int*>(0x400);

void writeToRegister(int value) {
*hardwareRegister = value; // Запись в регистр
}

int readFromRegister() {
return *hardwareRegister; // Чтение из регистра
}

int main() {
writeToRegister(100);
std::cout << "Register value: " << readFromRegister() << std::endl;
return 0;
}


Ставь 👍 и забирай 📚 Базу знаний
Post #2141 776
🤔 Какие есть операторы приведения типов?

1. static_cast: Для явного и безопасного приведения типов, известных на этапе компиляции.
2. dynamic_cast: Для приведения указателей или ссылок в иерархии классов с проверкой типов во время выполнения.
3. const_cast: Для снятия или добавления const и volatile квалификаторов.
4. reinterpret_cast: Для низкоуровневого преобразования указателей или других типов без проверки безопасности.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2140 848
🤔 Как называется одинаковый результат после применения хэш функции?

Одинаковый результат после применения хэш-функции называется коллизией.

🚩Что такое коллизия?

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

🚩Почему коллизии возникают?

🟠Ограниченный размер хэш-значений
Хэш-функция генерирует значения фиксированной длины (например, 32 или 64 бита), поэтому существует конечное количество возможных хэшей. Однако количество входных данных, которые можно подать на вход, теоретически бесконечно.
🟠Особенности алгоритма хэширования
Некоторые хэш-функции менее устойчивы к коллизиям из-за их структуры.

🚩Как справляться с коллизиями?

Есть несколько способов обработки коллизий в хэш-таблицах:

🟠Метод цепочек (chaining)
Каждое значение хэша хранит список элементов, которые получили одинаковый хэш. При добавлении нового ключа он просто добавляется в связанный список.
#include <iostream>
#include <list>
#include <vector>
using namespace std;

class HashTable {
int size;
vector<list<int>> table;

public:
HashTable(int s) : size(s), table(s) {}

int hashFunction(int key) {
return key % size;
}

void insert(int key) {
int index = hashFunction(key);
table[index].push_back(key);
}

void display() {
for (int i = 0; i < size; i++) {
cout << i << ": ";
for (int key : table[i]) {
cout << key << " -> ";
}
cout << "NULL" << endl;
}
}
};

int main() {
HashTable ht(5);
ht.insert(10);
ht.insert(15);
ht.insert(20);
ht.insert(7);
ht.insert(2);
ht.display();
return 0;
}


🟠Открытая адресация (open addressing)
При коллизии новое значение записывается в следующую свободную ячейку в таблице. Используются разные стратегии, такие как линейное пробирование, квадратичное пробирование или двойное хэширование.

🟠Использование криптографических хэш-функций
Устойчивые хэш-функции (например, SHA-256) уменьшают вероятность коллизий, но они медленнее и чаще используются в безопасности.

Ставь 👍 и забирай 📚 Базу знаний
Post #2139 700
🤔 Как сделать так, чтобы в shared_ptr были утечки?

Утечки возможны при создании циклических ссылок, например, когда два объекта хранят shared_ptr друг на друга.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2137 819
🤔 Асимптотическая сложность в list?

std::list — это двусвязный список, где каждый элемент хранит ссылку на предыдущий и следующий элементы. Это даёт эффективное добавление и удаление элементов в любой части списка, но делает доступ по индексу медленным

🚩Разбор операций в `std::list` с примерами

Добавление в начало и конец — O(1)
std::list<int> lst;
lst.push_back(10); // O(1)
lst.push_front(5); // O(1)


Доступ по индексу — O(n)
auto it = std::next(lst.begin(), 2); // O(n), приходится идти от начала
std::cout << *it << std::endl;


Вставка и удаление по итератору — O(1)
auto it = lst.begin();
std::advance(it, 1); // Двигаем итератор на 1 элемент (O(n))

lst.insert(it, 8); // O(1), просто меняем указатели
lst.erase(it); // O(1), просто изменяем ссылки соседних элементов


Поиск элемента — O(n)
auto it = std::find(lst.begin(), lst.end(), 8); // O(n)


Сортировка — O(n log n)
lst.sort(); // O(n log n), потому что используется сортировка слиянием


Ставь 👍 и забирай 📚 Базу знаний
Post #2136 731
🤔 Какой имеется побочный эффект при работе с кодом в хидере?

При работе с кодом в хидерах возможны проблемы, связанные с повторным включением файлов (multiple inclusion), что может вызвать ошибки компиляции. Это решается использованием включающих защит (#pragma once или #ifndef). Также код в хидере увеличивает время компиляции, так как включается в несколько исходных файлов.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚Базу знаний
Post #2135 846
🤔 Как убедиться что нет багов в коде?

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

🟠Внимательная проверка кода (Code Review)
Перед тем как запускать код, полезно просмотреть его вручную или с коллегами. Это помогает:
Найти логические ошибки
Улучшить читаемость кода
Проверить соответствие код-стайлу
// Опытный разработчик может заметить, что деление на 0 возможно
int divide(int a, int b) {
return a / b; // Потенциальная ошибка, если b == 0
}


Лучше добавить проверку
int divide(int a, int b) {
if (b == 0) {
throw std::runtime_error("Деление на ноль!");
}
return a / b;
}


🟠Юнит-тестирование (Unit Testing)
Тесты проверяют каждую функцию отдельно, чтобы убедиться, что она работает правильно. В C++ используют Google Test (gtest), Catch2 или Boost.Test.
#include <gtest/gtest.h>
#include "math_utils.h"

TEST(DivideFunctionTest, HandlesZeroDenominator) {
EXPECT_THROW(divide(10, 0), std::runtime_error);
}

TEST(DivideFunctionTest, NormalCase) {
EXPECT_EQ(divide(10, 2), 5);
}


🟠Отладка (Debugging)
Если код работает не так, как ожидалось, используют отладчики (GDB, LLDB, Visual Studio Debugger). Они позволяют пошагово исполнять код и смотреть, где происходит ошибка.
g++ -g main.cpp -o program  # Компилируем с отладочной информацией
gdb ./program # Запускаем отладчик


🟠Статический анализ кода
Используются специальные инструменты, которые автоматически ищут ошибки в коде:
Clang-Tidy
Cppcheck
SonarQube
cppcheck --enable=all my_code.cpp


🟠Динамический анализ (Sanitizers, Valgrind)
Эти инструменты помогают находить утечки памяти, переполнения буфера и другие ошибки.
g++ -fsanitize=address -g main.cpp -o program
./program # Если есть проблемы, они будут найдены


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

Ставь 👍 и забирай 📚 Базу знаний
  • 👍 2
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 →