«Why should C++ programmers minimize use of 'new'?»: analisando a pergunta icônica
Se você já visitou o Stack Overflow em busca de respostas sobre C++, certamente já viu esta pergunta. Ela acumulou milhares de votos e dezenas de milhares de visualizações. E não é à toa. Para um iniciante, o tema parece contraintuitivo: «Como assim? Em Java ou C# eu escrevo new constantemente, e isso é normal. Por que em C++ isso de repente é ruim?»
Hoje, não vamos apenas traduzir a resposta do fórum em inglês. Vamos detalhar minuciosamente por que minimizar new em C++ não é um capricho de guru, mas uma questão de performance, segurança e bom senso. E sim, vamos escrever código, olhar para o assembly (quase) e contar nanossegundos.
Para onde vai a memória: pilha vs heap
Antes de falar sobre new, vamos relembrar o fundamento. Em C++, existem dois lugares principais para armazenar dados:
- Pilha (stack) — pequena, rápida, gerenciada automaticamente. Assim que a variável sai do escopo, ela é destruída. Instantaneamente.
- Heap (heap) — grande, lenta, requer gerenciamento manual. Para obter memória lá, é necessário chamar
new(oumalloc). Para liberar —delete.
Cada chamada a new não é apenas «me dê memória». É uma chamada de sistema complexa, busca por um bloco livre, atualização de estruturas de controle. É centenas de vezes mais lento do que a alocação na pilha.
Exemplo #1: Comparação de velocidade
#include <chrono>#include <iostream>#include <vector>
struct Point { double x, y, z;};
int main() { const int N = 1000000;
// Alocação na pilha (array na pilha) auto start = std::chrono::high_resolution_clock::now(); Point points_stack[1000]; // 1000 objetos na pilha auto end = std::chrono::high_resolution_clock::now(); std::cout << "Stack time: " << std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count() << " ns";
// Alocação dinâmica via new start = std::chrono::high_resolution_clock::now(); std::vector<Point*> points_heap; for (int i = 0; i < 1000; ++i) { points_heap.push_back(new Point()); } end = std::chrono::high_resolution_clock::now(); std::cout << "Heap (new) time: " << std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count() << " ns";
// Limpamos (obrigatório!) for (auto p : points_heap) delete p;
return 0;}Resultado (em uma máquina típica):
Stack time: 0 nsHeap (new) time: 45200 nsDiferença de dezenas de milhares de vezes. Claro, é um teste sintético, mas mostra claramente: cada new desnecessário são microssegundos a menos no seu orçamento de performance.
O principal problema do new: vazamentos e fragmentação
Se fosse apenas uma questão de velocidade, ainda seria tolerável. Mas new traz consigo uma série de problemas:
1. Vazamentos de memória (memory leaks)
Esqueceu de chamar delete? Parabéns, você tem um vazamento. O programa funciona por uma hora, um dia, uma semana — e de repente trava com std::bad_alloc. Clássico. Especialmente perigosos são vazamentos em processos longos (servidores, jogos, GUI).
2. Ponteiros pendentes (dangling pointers)
Você deletou o objeto, mas o ponteiro permaneceu. Qualquer acesso a ele é comportamento indefinido (UB). O programa pode funcionar, pode travar, pode substituir sua música por techno. UB é maligno.
3. Fragmentação do heap
Chamadas frequentes a new/delete fragmentam o heap em pequenos pedaços. Mesmo que haja memória suficiente, alocar um bloco grande se torna impossível. Isso é chamado de fragmentação externa.