🆕 new e delete · Alocação Dinâmica em C++

🆕 Criando Variáveis em Tempo de Execução

"Os operadores new e delete permitem alocar e liberar memória dinamicamente no heap durante a execução do programa, oferecendo flexibilidade para criar estruturas de tamanho variável."

Enquanto variáveis locais e globais têm seu tamanho e tempo de vida definidos em tempo de compilação, a alocação dinâmica permite criar e destruir variáveis conforme a necessidade durante a execução. Isso é essencial para estruturas como listas, árvores, vetores de tamanho variável e qualquer situação onde a quantidade de dados só é conhecida em tempo de execução.

📌 O Operador new

O operador new aloca memória no heap para uma variável ou objeto e retorna um ponteiro para o endereço alocado.

// Alocando um único inteiro int* p = new int; // Aloca espaço para um int (valor indefinido) *p = 42; // Atribui valor // Alocando e inicializando em uma única linha int* q = new int(100); // Aloca e inicializa com 100 // Alocando com inicialização uniforme (C++11) int* r = new int{200}; // Aloca e inicializa com 200 // Alocando outros tipos float* f = new float(3.14f); char* c = new char('A'); bool* b = new bool(true);

🗑️ O Operador delete

O operador delete libera a memória previamente alocada com new, devolvendo-a ao heap. Esquecer de liberar memória causa vazamento (memory leak).

int* p = new int(42); // ... usa o valor ... delete p; // Libera a memória p = nullptr; // Boa prática: evita dangling pointer // delete em ponteiro nulo é seguro (não faz nada) int* q = nullptr; delete q; // OK, nenhum efeito
⚠️ Regra de Ouro: Para cada new, deve existir um delete correspondente. Caso contrário, a memória alocada nunca será liberada, causando vazamento de memória (memory leak).

📚 Arrays Dinâmicos com new[] e delete[]

Para alocar arrays cujo tamanho só é conhecido em tempo de execução, use new[] e libere com delete[].

int tamanho; cout << "Quantos números deseja armazenar? "; cin >> tamanho; // Aloca um array de inteiros no heap int* arr = new int[tamanho]; // Inicializa e usa o array for (int i = 0; i < tamanho; i++) { arr[i] = i * 10; } // Libera o array (note o []) delete[] arr; arr = nullptr;
⚠️ Muito Cuidado! Usar delete em vez de delete[] para liberar um array causa comportamento indefinido e geralmente vazamento parcial de memória.

📌 Inicialização de Arrays Dinâmicos

// Inicialização com valor padrão (todos os elementos zerados) int* arr1 = new int[5](); // {0, 0, 0, 0, 0} // Inicialização com lista (C++11) int* arr2 = new int[5]{1, 2, 3, 4, 5}; // Inicialização parcial (restante é zerado) int* arr3 = new int[5]{1, 2}; // {1, 2, 0, 0, 0}

🏗️ Alocação de Objetos (Classes/Structs)

new e delete também funcionam com tipos definidos pelo usuário, invocando construtores e destrutores apropriadamente.

class Pessoa { public: std::string nome; int idade; Pessoa(const std::string& n, int i) : nome(n), idade(i) { cout << "Construtor chamado" << endl; } ~Pessoa() { cout << "Destrutor chamado" << endl; } }; // Alocação de um único objeto Pessoa* p1 = new Pessoa("João", 30); cout << p1->nome << " tem " << p1->idade << " anos" << endl; delete p1; // Invoca o destrutor antes de liberar a memória // Alocação de array de objetos (requer construtor padrão) Pessoa* grupo = new Pessoa[3]{ {"Ana", 25}, {"Carlos", 40}, {"Bia", 35} }; delete[] grupo; // Invoca o destrutor para cada elemento

⚠️ E se new Falhar?

Se não houver memória suficiente, new lança uma exceção std::bad_alloc. Alternativamente, pode-se usar new (nothrow) que retorna nullptr em caso de falha.

// Comportamento padrão: lança exceção try { int* p = new int[1000000000000]; } catch (std::bad_alloc& e) { cout << "Falha na alocação: " << e.what() << endl; } // Usando nothrow: retorna nullptr em vez de lançar exceção int* p = new (std::nothrow) int[1000000000000]; if (p == nullptr) { cout << "Falha na alocação (nothrow)" << endl; }

🛡️ Smart Pointers: Uma Alternativa Mais Segura

Em C++ moderno, recomenda-se o uso de smart pointers (std::unique_ptr, std::shared_ptr) que gerenciam a memória automaticamente, eliminando a necessidade de chamar delete manualmente.

#include <memory> // unique_ptr: propriedade exclusiva std::unique_ptr<int> p1 = std::make_unique<int>(42); // Não precisa de delete! Memória liberada automaticamente // unique_ptr para array std::unique_ptr<int[]> arr = std::make_unique<int[]>(5); arr[0] = 10; // delete[] automático ao sair do escopo

📊 new/delete vs. malloc/free (C)

Característicanew / delete (C++)malloc / free (C)
Chama construtores/destrutores✅ Sim❌ Não
Retorna tipo correto✅ Sim (ponteiro tipado)❌ Retorna void* (precisa cast)
Tamanho da alocaçãoCalculado automaticamenteDeve ser especificado em bytes
Falha na alocaçãoLança exceção (ou nullptr com nothrow)Retorna NULL
Uso recomendadoC++C (evitar em C++)
💡 Boas Práticas com Alocação Dinâmica:
  • ✅ Prefira smart pointers (unique_ptr, shared_ptr) a new/delete manuais.
  • ✅ Use new/delete apenas quando absolutamente necessário.
  • ✅ Sempre emparelhe new com delete e new[] com delete[].
  • ✅ Atribua nullptr após delete para evitar dangling pointers.
  • ✅ Considere containers da STL (std::vector, std::string) em vez de arrays dinâmicos manuais.

🔗 Conclusão

Os operadores new e delete fornecem controle granular sobre a alocação de memória em C++. Embora sejam ferramentas poderosas, exigem disciplina e atenção para evitar vazamentos e comportamentos indefinidos. Em C++ moderno, o uso de smart pointers e containers da STL é fortemente recomendado, relegando new/delete a casos específicos de baixo nível ou implementação de estruturas de dados personalizadas.

⏭️ Próximo: Ponteiros para tipos derivados...

Postar um comentário

0 Comentários