Sobes.tech
Middle
204

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

Отговор от AI

sobes.tech AI

Изтичане на памет при използване на std::unique_ptr може да се случи в следните случаи:

  1. Неправилна употреба на собствен deleter: Ако персонализираният deleter не освобождава правилно заделения ресурс (например, delete вместо delete[] за масиви или не извиква съответната функция за освобождаване).

    #include <memory>
    #include <iostream>
    
    struct MyDeleter {
        void operator()(int* ptr) const {
            // ГРЕШКА: трябва да е delete[], а не delete
            delete ptr;
            std::cout << "Грешен deleter е извикан" << std::endl;
        }
    };
    
    int main() {
        // Заделяне на масив, но използване на грешен deleter
        std::unique_ptr<int, MyDeleter> ptr(new int[10], MyDeleter());
        // Паметта, заделена като масив, няма да бъде напълно освободена при изход от обхвата
        return 0;
    }
    
  2. Прехвърляне на суров указател от unique_ptr към функция, която по-късно приема собствеността и не я освобождава: Собствеността трябва да се прехвърля явно с std::move. Ако предадете get() или суров указател и след това не го освободите, може да възникне изтичане.

    #include <memory>
    #include <iostream>
    
    void process_and_lose(int* raw_ptr) {
        // Функцията "забравя" delete raw_ptr
        std::cout << "Обработка " << *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 беше ресетиран или преназначен преди извикването, щеше да има изтичане
        return 0;
    }
    

    Коригиран пример с потенциално изтичане (ако прехвърляте собствеността, но функцията не я освобождава):

    #include <memory>
    #include <iostream>
    
    void function_that_takes_ownership_and_leaks(int* owned_ptr) {
        // Предполага се, че owned_ptr сега принадлежи на тази функция
        std::cout << "Вземане на собственост и изтичане: " << *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 << "Ресурс създаден" << std::endl;} ~Resource() { std::cout << "Ресурс унищожен" << std::endl;} };
    void might_throw() { throw std::runtime_error("Опа!"); }
    
    int main() {
        Resource* res = new Resource(); // Заделяне на памет
        try {
            might_throw(); // Възниква изключение тук
            std::unique_ptr<Resource> ptr(res); // Тази линия няма да бъде достигната
        } catch (const std::runtime_error& e) {
            std::cerr << "Хванато изключение: " << e.what() << std::endl;
            // res* не е обвит в `unique_ptr` и не е изтрит
            // Това води до изтичане!
        }
        // Правилният начин: std::make_unique<Resource>() или std::unique_ptr<Resource> ptr(new Resource());
        return 0;
    }
    

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