Sobes.tech
Middle

How can a memory leak occur when using unique_ptr in C++?

sobes.tech AI

Answer from AI

Memory leaks when using std::unique_ptr can occur in the following cases:

  1. Incorrect use of a custom deleter: If the custom deleter does not properly release the allocated resource (for example, calling delete instead of delete[] for an array or not calling the appropriate resource release function).

    #include <memory>
    #include <iostream>
    
    struct MyDeleter {
        void operator()(int* ptr) const {
            // ERROR: should be delete[], not delete
            delete ptr;
            std::cout << "Called incorrect deleter" << std::endl;
        }
    };
    
    int main() {
        // Allocate an array but use an incorrect deleter
        std::unique_ptr<int, MyDeleter> ptr(new int[10], MyDeleter());
        // Memory allocated as an array will not be fully freed upon scope exit of ptr
        return 0;
    }
    
  2. Passing a raw pointer from unique_ptr to a function that then takes ownership and does not free it: Ownership transfer should be explicit using std::move. If you pass get() or a raw pointer and then do not handle freeing it elsewhere, it can lead to leaks.

    #include <memory>
    #include <iostream>
    
    void process_and_lose(int* raw_ptr) {
        // Function "forgets" to delete raw_ptr
        std::cout << "Processing " << *raw_ptr << std::endl;
        // Memory leak!
    }
    
    int main() {
        std::unique_ptr<int> ptr(new int(42));
        // Get raw pointer
        int* raw = ptr.get();
        process_and_lose(raw);
        // ptr still manages the memory, but raw_ptr is now a orphan
        // In this case, no leak occurs because ptr will free the memory
        // But if ptr was reset or reassigned before the call to process_and_lose,
        // and process_and_lose did not delete it, a leak would occur
        return 0;
    }
    

    Corrected example with potential leak (if ownership is transferred but the function does not delete):

    #include <memory>
    #include <iostream>
    
    void function_that_takes_ownership_and_leaks(int* owned_ptr) {
        // Assumes owned_ptr now belongs to this function
        std::cout << "Taking ownership and leaking: " << *owned_ptr << std::endl;
        // Leak: owned_ptr is not deleted
    }
    
    int main() {
        std::unique_ptr<int> ptr(new int(100));
        // Transfer ownership of raw pointer
        function_that_takes_ownership_and_leaks(ptr.release());
        // ptr is now empty, its resource (pointer) will not be automatically freed
        return 0;
    }
    
  3. Exceptions between memory allocation and wrapping in unique_ptr: Although std::make_unique prevents this, directly using new followed by constructing a unique_ptr can lead to leaks if an exception occurs between them.

    #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(); // Memory allocation
        try {
            might_throw(); // Exception occurs here
            std::unique_ptr<Resource> ptr(res); // This line will not be reached
        } catch (const std::runtime_error& e) {
            std::cerr << "Caught exception: " << e.what() << std::endl;
            // res* was not wrapped in unique_ptr and not deleted
            // This leads to a leak!
        }
        // Correct way: std::make_unique<Resource>() or std::unique_ptr<Resource> ptr(new Resource());
        return 0;
    }
    

Overall, memory leaks when using std::unique_ptr correctly are very unlikely, as its main purpose is automatic resource management, preventing leaks in typical scenarios. Issues arise when stepping outside its standard usage (custom deleters, release(), interaction with raw pointers).

How can a memory leak occur when using unique_ptr in… - sobes.tech