Sobes.tech
Middle

Como pode ocorrer uma fuga de memória ao usar unique_ptr em C++?

sobes.tech IA

Resposta da IA

A fuga de memória ao usar std::unique_ptr pode ocorrer nos seguintes casos:

  1. Uso incorreto do destruidor personalizado (Deleter): Se o destruidor personalizado não liberar corretamente o recurso alocado (por exemplo, chamando delete em vez de delete[] para um array ou não chamando a função de liberação adequada).

    #include <memory>
    #include <iostream>
    
    struct MyDeleter {
        void operator()(int* ptr) const {
            // ERRO: deve ser delete[], não delete
            delete ptr;
            std::cout << "Chamada ao destruidor incorreto" << std::endl;
        }
    };
    
    int main() {
        // Aloca um array, mas usa um destruidor incorreto
        std::unique_ptr<int, MyDeleter> ptr(new int[10], MyDeleter());
        // A memória alocada como array não será completamente liberada ao sair do escopo de ptr
        return 0;
    }
    
  2. Passar um ponteiro bruto de unique_ptr para uma função que assume posse e não libera: A transferência de posse deve ser feita explicitamente com std::move. Se você passar get() ou um ponteiro bruto e não cuidar de liberá-lo em outro lugar, isso pode causar vazamento.

    #include <memory>
    #include <iostream>
    
    void process_and_lose(int* raw_ptr) {
        // A função "esquece" de fazer delete raw_ptr
        std::cout << "Processando " << *raw_ptr << std::endl;
        // Vazamento de memória!
    }
    
    int main() {
        std::unique_ptr<int> ptr(new int(42));
        // Obter um ponteiro bruto
        int* raw = ptr.get();
        process_and_lose(raw);
        // ptr ainda gerencia a memória, mas raw_ptr se torna órfão
        // Neste caso, não há vazamento, pois ptr liberará a memória
        // Mas se ptr fosse resetado ou reatribuído antes da chamada a process_and_lose,
        // e a função não deletar, haveria vazamento
        return 0;
    }
    

    Exemplo corrigido com potencial vazamento (se transferirmos a posse, mas a função não deletar):

    #include <memory>
    #include <iostream>
    
    void function_that_takes_ownership_and_leaks(int* owned_ptr) {
        // Presume-se que owned_ptr agora pertence a esta função
        std::cout << "Tomando posse e vazando: " << *owned_ptr << std::endl;
        // Vazamento: owned_ptr não é deletado
    }
    
    int main() {
        std::unique_ptr<int> ptr(new int(100));
        // Transferir posse do ponteiro bruto
        function_that_takes_ownership_and_leaks(ptr.release());
        // ptr agora está vazio, seu recurso (ponteiro) não será liberado automaticamente
        return 0;
    }
    
  3. Exceções entre a alocação de memória e o encapsulamento em unique_ptr: Embora std::make_unique evite isso, usar diretamente new seguido do construtor unique_ptr pode causar vazamento se uma exceção ocorrer entre eles.

    #include <memory>
    #include <vector>
    
    class Resource { public: Resource() { std::cout << "Recurso criado" << std::endl;} ~Resource() { std::cout << "Recurso destruído" << std::endl;} };
    void might_throw() { throw std::runtime_error("Opa!"); }
    
    int main() {
        Resource* res = new Resource(); // Alocação de memória
        try {
            might_throw(); // Uma exceção ocorre aqui
            std::unique_ptr<Resource> ptr(res); // Esta linha não será atingida
        } catch (const std::runtime_error& e) {
            std::cerr << "Exceção capturada: " << e.what() << std::endl;
            // res* não foi envolvido em um unique_ptr e não foi deletado
            // Isso leva a um vazamento!
        }
        // Forma correta: std::make_unique<Resource>() ou std::unique_ptr<Resource> ptr(new Resource());
        return 0;
    }
    

De modo geral, o vazamento de memória ao usar std::unique_ptr corretamente é extremamente improvável, pois seu objetivo principal é gerenciar recursos automaticamente, prevenindo vazamentos em cenários típicos. Problemas surgem quando se sai do uso padrão (destructores personalizados, release(), interação com ponteiros brutos).