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

Как можно допустить утечку памяти при использовании unique_ptr в C++?

Sobes Copilot

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

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

sobes.tech AI

Утечка памяти при использовании std::unique_ptr возможна в следующих случаях:

  1. Некорректное использование собственной реализации удалителя (Deleter): Если пользовательский удалитель не освобождает выделенный ресурс должным образом (например, вызывает delete вместо delete[] для массива или не вызывает соответствующую функцию освобождения ресурса).

    #include <memory>
    #include <iostream>
    
    struct MyDeleter {
        void operator()(int* ptr) const {
            // ОШИБКА: Должен быть delete[], а не delete
            delete ptr;
            std::cout << "Called incorrect deleter" << std::endl;
        }
    };
    
    int main() {
        // Выделяем массив, но используем некорректный удалитель
        std::unique_ptr<int, MyDeleter> ptr(new int[10], MyDeleter());
        // Память, выделенная как массив, не будет освобождена полностью при выходе из области видимости ptr
        return 0;
    }
    
  2. Передача сырого указателя из unique_ptr в функцию, которая затем принимает владение им и не освобождает: Передача владения должна выполняться явно с помощью std::move. Если вы передаете get() или сырой указатель, а затем не заботитесь об освобождении в другом месте, это может привести к утечке.

    #include <memory>
    #include <iostream>
    
    void process_and_lose(int* raw_ptr) {
        // Функция "забывает" delete raw_ptr
        std::cout << "Processing " << *raw_ptr << std::endl;
        // Утечка памяти!
    }
    
    int main() {
        std::unique_ptr<int> ptr(new int(42));
        // Получаем сырой указатель
        int* raw = ptr.get();
        process_and_lose(raw);
        // ptr все еще управляет памятью, но raw_ptr стал сиротой
        // В данном случае утечки нет, потому что ptr освободит память
        // Но если бы ptr был сброшен или переназначен до вызова process_and_lose,
        // и process_and_lose не удалил его, утечка была бы
        return 0;
    }
    

    Исправленный пример с потенциальной утечкой (если передаем владение, но функция не удаляет):

    #include <memory>
    #include <iostream>
    
    void function_that_takes_ownership_and_leaks(int* owned_ptr) {
        // Предполагается, что owned_ptr теперь принадлежит этой функции
        std::cout << "Taking ownership and leaking: " << *owned_ptr << std::endl;
        // Утечка: owned_ptr не удаляется
    }
    
    int main() {
        std::unique_ptr<int> ptr(new int(100));
        // Передаем владение сырому указателю
        function_that_takes_ownership_and_leaks(ptr.release());
        // ptr теперь пуст, его ресурс (указатель) не будет освобожден автоматически
        return 0;
    }
    
  3. Исключения между выделением памяти и оборачиванием в unique_ptr: Несмотря на то, что std::make_unique предотвращает это, прямое использование new с последующим конструктором unique_ptr может привести к утечке, если между ними возникнет исключение.

    #include <memory>
    #include <vector>
    
    class Resource { public: Resource() { std::cout << "Resource created" << std::endl;} ~Resource() { std::cout << "Resource destroyed" << std::endl;} };
    void might_throw() { throw std::runtime_error("Oops!"); }
    
    int main() {
        Resource* res = new Resource(); // Выделение памяти
        try {
            might_throw(); // Исключение возникает здесь
            std::unique_ptr<Resource> ptr(res); // Эта строка не будет достигнута
        } catch (const std::runtime_error& e) {
            std::cerr << "Caught exception: " << e.what() << std::endl;
            // res* не был обернут в unique_ptr и не был удален
            // Это приводит к утечке!
        }
        // Корректный способ: std::make_unique<Resource>() или std::unique_ptr<Resource> ptr(new Resource());
        return 0;
    }
    

В целом, утечка памяти при корректном использовании std::unique_ptr крайне маловероятна, поскольку его основная цель — автоматическое управление ресурсами, предотвращая утечки в типичных сценариях. Проблемы возникают при выходе за рамки его стандартного использования (собственные удалители, release(), взаимодействие с сырыми указателями).