Ponteiros

Os ponteiros são um dos conceitos mais fundamentais e poderosos da linguagem C++, permitindo ao programador manipular diretamente endereços de memória. Em termos simples, um ponteiro é uma variável cujo valor é o endereço de outra variável na memória do computador.

Isso permite manipular diretamente a memória, aceder e modificar valores de variáveis de forma indireta, além de possibilitar a alocação dinâmica de memória e o uso eficiente de arrays e funções.

Declaração e Inicialização

Para declarar um ponteiro, utiliza-se o operador *:

int *ptr; // Ponteiro para inteiro
double *dptr; // Ponteiro para double

Para inicializar um ponteiro, atribui-se o endereço de uma variável usando o operador &:

int valor = 10;
int *ptr = &valor; // ptr aponta para valor

Aceder a Valores com Ponteiros

O operador * (desreferenciação) permite aceder o valor armazenado no endereço apontado pelo ponteiro:

int valor = 20;
int *ptr = &valor;
cout << *ptr << endl; // Imprime 20

Se alterarmos o valor via ponteiro, a variável original também é alterada:

*ptr = 30;
cout << valor << endl; // Imprime 30

Ponteiros e Arrays

O nome de um array é, na prática, um ponteiro para o seu primeiro elemento:

int arr[3] = {1, 2, 3};
int *ptr = arr;
cout << ptr[1] << endl; // Imprime 2

É possível percorrer arrays usando aritmética de ponteiros:

for (int i = 0; i < 3; i++) {
cout << *(ptr + i) << endl;
}

Alocação Dinâmica de Memória

Ponteiros são essenciais para alocação dinâmica de memória, usando os operadores new e delete:

int *p = new int; // Aloca um inteiro
*p = 42;
cout << *p << endl; // Imprime 42
delete p; // Liberta a memória

Para arrays dinâmicos:

int *v = new int[5]; // Aloca array de 5 inteiros
for (int i = 0; i < 5; i++) v[i] = i * 2;
delete[] v; // Liberta o array

Passar ponteiros para Funções

Ponteiros permitem passar variáveis por referência para funções, possibilitando a modificação do valor original:

void incrementa(int *p) {
(*p)++;
}

int main() {
int x = 5;
incrementa(&x);
cout << x << endl; // Imprime 6
}

Ponteiros para Funções

É possível criar ponteiros que apontam para funções, permitindo selecionar dinamicamente qual função executar:

int soma(int a, int b) { return a + b; }
int subtrai(int a, int b) { return a - b; }

int main() {
int (*operacao)(int, int);
operacao = soma;
cout << operacao(2, 3) << endl; // Imprime 5
operacao = subtrai;
cout << operacao(5, 2) << endl; // Imprime 3
}

Ponteiros inteligentes (smart pointers)

Smart pointers em C++ são classes que encapsulam ponteiros brutos, gerenciando automaticamente a alocação e liberação de memória. Eles ajudam a evitar problemas comuns como vazamento de memória, ponteiros pendentes e uso de memória já libertada, tornando o código mais seguro e robusto.

Tipos principais de smart pointers

  • std::unique_ptr: Possui exclusivamente o objeto apontado. Não pode ser copiado, apenas movido.
  • std::shared_ptr: Permite múltiplos ponteiros compartilharem a posse do mesmo recurso, usando contagem de referências.
  • std::weak_ptr: Observa um objeto gerido por shared_ptr, mas não participa da contagem de referências, evitando ciclos.

Exemplos

unique_ptr

#include <iostream>
#include <memory>

int main() {
std::unique_ptr<int> ptr = std::make_unique<int>(42);
std::cout << *ptr << std::endl; // Saída: 42
} // ptr é destruído automaticamente aqui e a memória é libertada

shared_ptr

#include <iostream>
#include <memory>

int main() {
std::shared_ptr<int> ptr1 = std::make_shared<int>(10);
{
std::shared_ptr<int> ptr2 = ptr1;
std::cout << ptr1.use_count() << std::endl; // Saída: 2
} // ptr2 sai de escopo, contagem volta para 1
std::cout << ptr1.use_count() << std::endl; // Saída: 1
} // ptr1 destruído, memória libertada

weak_ptr

#include <iostream>
#include <memory>

int main() {
std::shared_ptr<int> sp = std::make_shared<int>(20);
std::weak_ptr<int> wp = sp; // wp observa sp, mas não impede a libertação
if (auto spt = wp.lock()) { // lock retorna shared_ptr se o objeto ainda existir
std::cout << *spt << std::endl; // Saída: 20
}
}

Vantagens

  • Gestão automática de memória: O objeto é destruído automaticamente quando não há mais ponteiros inteligentes apontando para ele.
  • Segurança contra vazamentos: Reduz drasticamente o risco de esquecer de libertar memória.
  • Sintaxe semelhante a ponteiros tradicionais: Operadores * e -> são sobrecarregados, facilitando o uso.

Cuidados com Ponteiros

  • Ponteiros não inicializados podem causar erros graves.
  • Liberte sempre a memória alocada dinamicamente com delete ou delete[].
  • Evite aceder memória já libertada (ponteiros soltos).
  • Prefira ponteiros inteligentes (std::unique_ptr, std::shared_ptr) no C++ moderno para maior segurança.

Resumo

  • Ponteiros são variáveis que armazenam endereços de memória.
  • Permitem manipulação eficiente de arrays, funções e alocação dinâmica.
  • Exigem atenção para evitar vazamentos de memória e acessos inválidos.
  • São fundamentais para programação eficiente e flexível em C++.