Назад к вопросам
Middle+
167
questionbank

Что такое строгая гарантия исключений и как она реализуется в C++?

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

Строгая гарантия исключений (Strong Exception Guarantee) означает, что в случае возникновения исключения состояние программы остается неизменным, как если бы операция никогда не выполнялась.

Реализуется чаще всего с помощью идиомы Copy-and-Swap:

  1. Создается неявная или явная копия объекта.
  2. Операция выполняется с копией.
  3. Если операция успешна, состояние исходного объекта атомарно обменивается с состоянием копии (обычно через swap).
#include <algorithm> // для std::swap

class MyClass {
private:
    int* data;
    size_t size;

public:
    // Конструктор по умолчанию
    MyClass() : data(nullptr), size(0) {}

    // Конструктор копирования
    MyClass(const MyClass& other) : size(other.size) {
        data = new int[size]; // Выделение памяти может бросить исключение bad_alloc
        std::copy(other.data, other.data + size, data); // Копирование может бросить исключение
    }

    // Оператор присваивания с использованием идиомы Copy-and-Swap для строгой гарантии
    MyClass& operator=(MyClass other) { // Параметр передается по значению, что запускает конструктор копирования
        swap(*this, other); // Атомарный обмен состоянием с временным объектом
        return *this;
    }

    // Функция swap
    friend void swap(MyClass& first, MyClass& second) noexcept { // swap не должен бросать исключения
        using std::swap; // Важно для ADL
        swap(first.data, second.data);
        swap(first.size, second.size);
    }

    // Конструктор перемещения
    MyClass(MyClass&& other) noexcept
        : data(other.data), size(other.size) {
        other.data = nullptr;
        other.size = 0;
    }

    // Деструктор
    ~MyClass() {
        delete[] data;
    }

    // Пример метода, который может бросить исключение
    void resize(size_t new_size) {
        // Создаем временный объект с новым размером
        MyClass temp;
        temp.size = new_size;
        temp.data = new int[new_size]; // Может бросить исключение

        // Копируем существующие данные (или часть их)
        size_t copy_size = std::min(size, new_size);
        std::copy(data, data + copy_size, temp.data); // Может бросить исключение

        // Если дошли до сюда, операция прошла успешно.
        // Атомарно обмениваем состояние.
        swap(*this, temp);
    }

    // Пример метода, изменяющего состояние, который может нуждаться в строгой гарантии
    void modify_element(size_t index, int value) {
        if (index >= size) {
            // Выход за границы, может бросить исключение или обработать иначе
            throw std::out_of_range("Index out of bounds");
        }
        // Прямое изменение без строгой гарантии для этой простой операции
        // Для более сложных операций, включающих несколько шагов с потенциальными исключениями,
        // могла бы потребоваться Copy-and-Swap.
        data[index] = value;
    }

    // Другие методы...
};

Принцип: все потенциально бросающие исключения операции выполняются на копии. Если исключение происходит, копия (недоделанная) уничтожается, а исходный объект остается в прежнем состоянии. Если операции на копии проходят успешно, состояние исходного объекта обновляется в одном атомарном, не бросающем исключений шаге (обычно swap).