Introdução
C++ é uma linguagem de programação poderosa e flexível que dá ao desenvolvedor enorme controle sobre os recursos do sistema. No entanto, essa liberdade vem com grande responsabilidade. Programadores iniciantes frequentemente cometem os mesmos erros que podem levar a bugs difíceis de encontrar, vazamentos de memória e comportamento indefinido. Neste artigo, veremos os principais erros de desenvolvedores iniciantes em C++ e mostraremos como evitá-los.
1. Vazamentos de memória e gerenciamento incorreto de recursos
Problema: gerenciamento manual de memória via new/delete
Um dos erros mais comuns é esquecer de chamar delete para um objeto criado via new. Isso leva a um vazamento de memória.
void example() { int* ptr = new int(42); // ... algum código ... // Esqueceu-se do delete ptr;}Em casos mais complexos, se ocorrer uma exceção entre new e delete, a memória também não é liberada.
Solução: use RAII e ponteiros inteligentes
O C++11 e padrões mais recentes fornecem ponteiros inteligentes que gerenciam automaticamente o tempo de vida de um objeto.
#include <memory>
void example() { std::unique_ptr<int> ptr = std::make_unique<int>(42); // A memória será automaticamente liberada ao sair do escopo}Para arrays, use std::vector em vez de gerenciamento manual:
// Ruim:int* arr = new int[100];// ... trabalho ...delete[] arr;
// Bom:std::vector<int> arr(100);// A memória é gerenciada automaticamente2. Comportamento Indefinido
Problema: acesso a índice de array fora dos limites
C++ não tem verificação de limites embutida para arrays brutos. Acessar um elemento fora do array é comportamento indefinido clássico.
int arr[5] = {1, 2, 3, 4, 5};arr[10] = 42; // Comportamento indefinido! Pode "funcionar", pode travarSolução: use std::array e std::vector com at()
#include <vector>#include <array>
std::array<int, 5> arr = {1, 2, 3, 4, 5};// arr.at(10) lançará exceção std::out_of_range
std::vector<int> vec = {1, 2, 3};vec.at(5); // Verificação segura de limitesProblema: uso de variáveis não inicializadas
int x;std::cout << x; // Comportamento indefinido: x não foi inicializadoSempre inicialize variáveis:
int x = 0;int* ptr = nullptr;3. Confusão entre ponteiros e referências
Problema: retornar um ponteiro para um objeto local
int* getBadPointer() { int local = 42; return &local; // Erro! local é destruído ao sair da função}Isso leva a um ponteiro solto. Usar tal ponteiro é comportamento indefinido.
Solução: retorne por valor ou use ponteiros inteligentes
// Retornar por valor (preferido para tipos simples)int getValue() { return 42;}
// Retornar ponteiro inteligente para objetos dinâmicosstd::unique_ptr<MyClass> createObject() { return std::make_unique<MyClass>();}Problema: confusão entre referências e ponteiros
Referências não podem ser nullptr e não podem ser reatribuídas. Ponteiros podem. Iniciantes frequentemente tentam passar nullptr para uma função que espera uma referência.
void process(int& ref) { // ref tem garantia de se referir a um objeto existente}
int* ptr = nullptr;// process(*ptr); // Erro! Desreferenciar nullptr é UBUse referências quando o objeto for obrigatório, e ponteiros (ou std::optional) quando puder ser null.