C++: Alocação Dinâmica de Memória
Última atualização: 2026-08-26
Nas aulas anteriores, variáveis e arrays eram alocados na pilha — tamanho fixo, liberados automaticamente quando a função termina.
Mas se você precisa armazenar uma quantidade incerta de dados (ex: o usuário digita 1000 inteiros, mas você não sabe no momento da compilação), você precisa de alocação dinâmica de memória — alocando memória no heap.
1. Pilha vs Heap
(1) 1.1 Duas Regiões de Memória
| Comparação | Pilha (Stack) | Heap |
|---|---|---|
| Momento da alocação | Determinado em tempo de compilação | Determinado em tempo de execução |
| Tamanho | Fixo (determinado em tempo de compilação) | Variável (solicitado em tempo de execução) |
| Momento da liberação | Automático (quando a função termina) | Manual (usando delete) |
| Eficiência | Alta | Baixa |
| Casos de uso | Variáveis locais, arrays | Dados de tamanho dinâmico |
(2) 1.2 Analogia do Dia a Dia
| Analogia da Vida Real | Equivalente em Programa |
|---|---|
| Restaurante de fast food (cardápio fixo, comida servida imediatamente após o pedido) | Pilha |
| Buffet (pegue o quanto quiser) | Heap |
2. new e delete
(1) 2.1 Alocando Memória com new
Sintaxe:
▶ Exemplo 2: Gerenciamento Dinâmico de Memória (Dificuldade ⭐)
Type* nomePonteiro = new Type;
Saída:
(Saída do programa)
Exemplo:
#include <iostream>
int main() {
// Aloca um int no heap
int* p = new int; // p Apontando para int no heap
*p = 5; // Atribui valor ao int no heap
std::cout << "*p = " << *p << std::endl; // 5
return 0;
}
💡 Ponto-chave: new retorna um ponteiro — apontando para a memória alocada no heap.
(2) 2.2 Liberando Memória com delete
Sintaxe:
delete nomePonteiro;
Exemplo:
#include <iostream>
int main() {
int* p = new int;
*p = 5;
std::cout << "*p = " << *p << std::endl;
delete p; // ✅ Libera memória do heap
p = nullptr; // ✅ Boa prática:Definir como nullptr após liberar
return 0;
}
💡 Regra de ouro: new e delete devem sempre vir em pares — todo new precisa de um delete.
(3) 2.3 Esquecer delete Causa Vazamento de Memória
Exemplo de erro:
#include <iostream>
void foo() {
int* p = new int;
*p = 5;
// ❌ Esqueceu de escrever delete p;
} // Função termina, p é destruído, mas o int no heap não é liberado (vazamento de memória)
int main() {
for (int i = 0; i < 1000000; i++) {
foo(); // Cada chamada vaza 4 bytes
}
// Uso de memória do programa continua crescendo, pode eventualmente falhar
return 0;
}
💡 Vazamento de memória: Memória alocada no heap que nunca é liberada e não pode mais ser acessada (porque o ponteiro para ela foi destruído).
3. Arrays Dinâmicos
(1) 3.1 Alocando Arrays com new
Sintaxe:
Type* nomePonteiro = new Type[tamanho];
Exemplo:
#include <iostream>
int main() {
int n;
std::cout << "Por favor, digite o tamanho do array: ";
std::cin >> n;
// Aloca um array de n ints no heap
int* arr = new int[n];
// Usando o array
for (int i = 0; i < n; i++) {
arr[i] = i + 1;
}
// Exibe o array
for (int i = 0; i < n; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
// Libera array(Nota:Use delete[], não delete)
delete[] arr;
return 0;
}
💡 Ponto-chave: Para liberar um array, use delete[] (com colchetes), não delete!
(2) 3.2 new e delete Devem Ser Emparelhados Corretamente
| Alocação | Liberação |
|---|---|
new int |
delete p; |
new int[10] |
delete[] p; |
Exemplo de erro:
int* p = new int[10];
delete p; // ❌ Erro: Deveria usar delete[] p;
4. Ponteiros Pendentes
(1) 4.1 O que é um Ponteiro Pendente?
Um ponteiro pendente é um ponteiro que aponta para memória já liberada.
#include <iostream>
int main() {
int* p = new int(5);
delete p; // Libera memória
// ❌ Perigoso: p agora é um ponteiro pendente
std::cout << *p << std::endl; // Comportamento indefinido! Pode exibir lixo ou falhar
return 0;
}
💡 Solução: Defina o ponteiro como nullptr imediatamente após liberar:
delete p;
p = nullptr; // ✅ Boa prática
5. Prática: Ordenando um Array Dinâmico
▶ Exemplo 1: Selection Sort com Array Dinâmico (Dificuldade ⭐⭐)
#include <iostream>
void selectionSort(int* arr, int n) {
for (int i = 0; i < n - 1; i++) {
int minIdx = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIdx]) {
minIdx = j;
}
}
int temp = arr[i];
arr[i] = arr[minIdx];
arr[minIdx] = temp;
}
}
int main() {
int n;
std::cout << "Por favor, digite o tamanho do array: ";
std::cin >> n;
int* arr = new int[n]; // Aloca array dinamicamente
std::cout << "Por favor, digite " << n << " Inteiros: " << std::endl;
for (int i = 0; i < n; i++) {
std::cin >> arr[i];
}
selectionSort(arr, n); // Ordena
std::cout << "Após ordenação: " << std::endl;
for (int i = 0; i < n; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
delete[] arr; // Libera memória
arr = nullptr;
return 0;
}
Saída:
Por favor, digite o tamanho do array:
Por favor, digite Inteiros:
Após ordenação:
Resultado da execução:
Por favor, digite o tamanho do array: 5
Por favor, digite 5 Inteiros:
5 3 1 4 2
Após ordenação:
1 2 3 4 5
6. Erros Comuns
(1) 6.1 Double delete
Exemplo de erro:
int* p = new int(5);
delete p;
delete p; // ❌ Erro: Liberação dupla da mesma memória (comportamento indefinido)
Correção: Defina como nullptr imediatamente após liberar (delete nullptr é seguro).
delete p;
p = nullptr;
(2) 6.2 Esquecer delete
Exemplo de erro:
int* arr = new int[10];
// ❌ Esqueceu de escrever delete[] arr;
Consequência: Vazamento de memória.
❓ Perguntas Frequentes
P: Por que usar memória dinâmica? Não posso simplesmente usar arrays? R: Se você sabe o tamanho em tempo de compilação, arrays funcionam. Mas se: > - O tamanho é determinado pela entrada do usuário > - O tamanho pode ser muito grande (excedendo a capacidade da pilha) > - Você precisa que os dados persistam após o término de uma função
Então você precisa de memória dinâmica.
P: Qual a diferença entre delete e delete[]? R:> -
deletelibera um único objeto > -delete[]libera um arraySe emparelhado incorretamente (ex:
new int[10]comdelete p), o comportamento é indefinido (pode liberar apenas o primeiro elemento, ou falhar).
P: Como posso evitar vazamentos de memória? R:> 1. Garanta que
newedeletevenham em pares > 2. Defina o ponteiro comonullptrimediatamente após liberar > 3. Use smart pointers (std::unique_ptr,std::shared_ptr, coberto mais tarde) — eles liberam memória automaticamente
▶ Exemplo 3: Array Dinâmico (Dificuldade ⭐)
#include <iostream>
int main() {
int size = 5;
int* arr = new int[size];
for (int i = 0; i < size; i++) {
arr[i] = i * 10;
}
for (int i = 0; i < size; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
delete[] arr;
return 0;
}
Saída:
0 10 20 30 40
new int[n] aloca um array, delete[] libera um array. Não esqueça o []!
- Pilha: alocada em tempo de compilação, liberada automaticamente, tamanho fixo
- Heap: alocado em tempo de execução, liberado manualmente (
delete), tamanho variável newaloca memória,deletelibera memórianew[]aloca arrays,delete[]libera arrays- Vazamento de memória: esquecer
delete, consequências sérias - Ponteiro pendente: aponta para memória liberada, perigoso
📖 Resumo
- new/delete: aloca/libera memória manualmente
- new[]/delete[]: alocação/liberação de memória de arrays
- Vazamento de memória: esquecer de liberar causa desperdício de recursos
- Smart pointers: recomenda-se usar unique_ptr/shared_ptr como alternativas
📝 Exercícios
-
Básico (Dificuldade ⭐): Use
newpara alocar umdouble, atribua3.14, exiba-o, depois libere-o. -
Intermediário (Dificuldade ⭐⭐): Deixe o usuário digitar
n, usenewpara alocar dinamicamente um arrayintde tamanhon, digiteninteiros, encontre o máximo, depois libere a memória. -
Desafio (Dificuldade ⭐⭐⭐): Implemente uma classe "array dinâmico" usando memória dinâmica (você aprenderá classes mais tarde; use uma struct por enquanto):
-
Inclua:
int* data(ponteiro para array no heap),int size(tamanho atual),int capacity(capacidade) -
Implemente uma função
push_back: sesize == capacity, realoque um bloco maior de memória (newum array maior, copie os dados, libere o antigo) -
Teste: adicione 100 elementos
- new aloca memória do heap e retorna um ponteiro
- delete libera memória alocada por new
- new[] aloca arrays, delete[] libera arrays
- Vazamento de memória: usar new sem delete
- Falha na alocação: new lança exceção std::bad_alloc
7. 🚀 Próximo Passo
Agora que você aprendeu alocação dinâmica de memória, vamos avançar para Ponteiros e Referências: Prática Abrangente (Aula 27) — reforçando tudo o que você aprendeu através de exemplos integrados!