Dominando Memória Dinâmica em C++: Stack vs Heap Explicados
Uma boa compreensão de como a memória dinâmica realmente funciona em C++ é essencial para se tornar um bom programador C++. A memória no seu programa C++ é dividida em duas partes:
A stack (pilha) — Todas as variáveis declaradas dentro da função ocuparão memória da stack.
A heap (heap) — Esta é memória não utilizada do programa e pode ser usada para alocar memória dinamicamente quando o programa roda.
Muitas vezes, você não sabe antecipadamente quanta memória precisará para armazenar informação particular em uma variável definida e o tamanho da memória necessária pode ser determinado em tempo de execução.
Você pode alocar memória em tempo de execução dentro da heap para a variável de um tipo dado usando um operador especial em C++ que retorna o endereço do espaço alocado. Este operador é chamado de operador new.
Se você não precisa mais de memória alocada dinamicamente, pode usar o operador delete, que desaloca memória previamente alocada pelo operador new.
Operadores new e delete
Existe a seguinte sintaxe genérica para usar o operador new para alocar memória dinamicamente para qualquer tipo de dado:
new data-type;
Aqui, data-type pode ser qualquer tipo de dado embutido incluindo array ou qualquer tipo de dado definido pelo usuário incluindo classe ou estrutura. Vamos começar com tipos de dados embutidos. Por exemplo, podemos definir um ponteiro para tipo double e então requisitar que a memória seja alocada em tempo de execução. Podemos fazer isso usando o operador new com as seguintes instruções:
double* pvalue = NULL; // Ponteiro inicializado com null pvalue = new double; // Requisição de memória para a variável
A memória pode não ter sido alocada com sucesso, se o free store tiver sido usado. Então é boa prática verificar se o operador new está retornando ponteiro NULL e tomar ação apropriada como abaixo:
double* pvalue = NULL;
if(!(pvalue = new double)) {
cout << "Error: out of memory." << endl;
exit(1);
}
A função malloc() do C ainda existe em C++, mas é recomendado evitar usar malloc(). A principal vantagem do new sobre malloc() é que new não apenas aloca memória, ele constrói objetos, que é o propósito principal do C++.
A qualquer momento, quando você sentir que uma variável alocada dinamicamente não é mais necessária, pode liberar a memória que ela ocupa no free store com o operador 'delete' da seguinte forma:
delete pvalue; // Libera memória apontada por pvalue
Vamos juntar os conceitos acima e formar o exemplo a seguir para mostrar como 'new' e 'delete' funcionam:
#include <iostream>
using namespace std;
int main() {
double* pvalue = NULL; // Ponteiro inicializado com null
pvalue = new double; // Requisição de memória para a variável
*pvalue = 29494.99; // Armazena valor no endereço alocado
cout << "Value of pvalue: " << *pvalue << endl;
delete pvalue; // Libera a memória
return 0;
}
Se compilarmos e executarmos o código acima, isso produziria o seguinte resultado:
Value of pvalue: 29495
Alocação Dinâmica de Memória para Arrays
Considere que você queira alocar memória para um array de caracteres, ou seja, string de 20 caracteres. Usando a mesma sintaxe que usamos acima, podemos alocar memória dinamicamente como mostrado abaixo:
char* pvalue = NULL; // Ponteiro inicializado com null pvalue = new char[20]; // Requisição de memória para a variável
Para remover o array que acabamos de criar, a instrução seria assim:
delete[] pvalue; // Deleta array apontado por pvalue
Seguindo a sintaxe genérica similar do operador new, você pode alocar para um array multi-dimensional da seguinte forma:
double** pvalue = NULL; // Ponteiro inicializado com null pvalue = new double[3][4]; // Aloca memória para array 3x4
No entanto, a sintaxe para liberar memória para array multi-dimensional permanece a mesma:
delete[] pvalue; // Deleta array apontado por pvalue
Alocação Dinâmica de Memória para Objetos
Objetos não são diferentes de tipos de dados simples. Por exemplo, considere o seguinte código onde vamos usar um array de objetos para esclarecer o conceito:
#include <iostream>
using namespace std;
class Box {
public:
Box() { cout << "Constructor called!" << endl; }
~Box() { cout << "Destructor called!" << endl; }
};
int main() {
Box* myBoxArray = new Box[4];
delete[] myBoxArray; // Deleta array
return 0;
}
Se você alocar um array de quatro objetos Box, o construtor simples será chamado quatro vezes e similarmente ao deletar esses objetos, o destrutor também será chamado o mesmo número de vezes.
Se compilarmos e executarmos o código acima, isso produziria o seguinte resultado:
Constructor called! Constructor called! Constructor called! Constructor called! Destructor called! Destructor called! Destructor called! Destructor called!
Linguagem C
- Entendendo Tipos de Dados em C++: Fundamentos, Modificadores e Boas Práticas
- Dominando Operadores em C++: Guia Completo com Exemplos Práticos
- Comentários em C++: Boas Práticas para Código Legível e Manutenível
- Gerenciamento Dinâmico de Memória em C: calloc, malloc e free Explicados
- Linguagem de Programação C++: Visão Geral e Principais Características
- Constantes e Literais em C++: Tipos, Sintaxe e Boas Práticas
- Guia Completo de Operadores em C++: Tipos, Uso e Boas Práticas
- Dominando Tipos Numéricos em C++: Guia Prático
- Entendendo Referências em C++: Conceitos-Chave e Diferenças de Ponteiros
- Dominando Memória Dinâmica em C++: Stack vs Heap Explicados