Introdução: por que a conversão de tipos em C++ é um campo minado
\n\nVocê já escreveu algo como (int)someFloat e esperou que tudo ficasse bem? Ou talvez já tenha se deparado com uma situação em que o programa compila, mas falha em tempo de execução com um erro misterioso? Bem-vindo ao mundo da conversão de tipos em C++ — um lugar onde até mesmo desenvolvedores experientes perdem horas depurando.
A pergunta "Regular cast vs. static_cast vs. dynamic_cast" no Stack Overflow acumulou dezenas de milhares de visualizações. E isso não é por acaso. Cada tipo de conversão é uma ferramenta com seu próprio propósito, e usar a ferramenta errada pode levar a comportamento indefinido, vazamentos de memória ou simplesmente a um código que não funciona.
\n\nNeste artigo, analisaremos três formas principais de conversão de tipos em C++, mostraremos os erros mais comuns e daremos recomendações claras sobre como evitá-los. Ao final, você saberá exatamente qual cast usar em cada situação específica.
\n\n1. Regular cast (C-style cast) — herança do passado
\n\nO bom e velho C-style cast é a forma mais simples, mas ao mesmo tempo a mais perigosa de conversão. Ele se parece com isto: (NewType)expression. Em C++, ele é considerado "regular", mas seu comportamento pode surpreender bastante.
Como funciona?
\nO compilador C++ tenta interpretar o C-style cast como uma sequência de static_cast, const_cast e reinterpret_cast. Ele escolhe a primeira combinação adequada. O problema é que você nunca sabe qual cast exatamente será aplicado no final.
Exemplo de erro: alteração acidental de constância
\nconst int value = 42;\nint* ptr = (int*)&value; // C-style cast remove const\n*ptr = 100; // Comportamento indefinido!\ncout << value; // Pode exibir 42, 100 ou qualquer coisa\n\nEste código compila sem erros, mas leva a um comportamento indefinido. const_cast aqui seria mais honesto — pelo menos mostraria sua intenção.
Quando o C-style cast ainda é aceitável?
\n- \n
- Nunca em C++ moderno (C++11 e posteriores). \n
- Apenas para conversões numéricas muito simples, onde o risco é mínimo (por exemplo,
(float)intValue), mas mesmo assim é melhor usarstatic_cast. \n
Conclusão: esqueça o C-style cast. Ele torna o código ilegível e inseguro. Compiladores modernos podem até emitir avisos para tais construções.
\n\n2. static_cast — o soldado universal
\n\nstatic_cast é o tipo de conversão mais usado em C++. Ele realiza a verificação em tempo de compilação e é adequado para a maioria das situações em que a conversão de tipos é logicamente correta.
Quando usar static_cast?
\n- \n
- Conversão de tipos numéricos:
double→int,int→float. \n - Upcast (conversão de ponteiro de classe derivada para ponteiro de classe base). \n
- Conversão explícita de
void*de volta para um tipo específico. \n - Chamada de construtores com um argumento (mas é melhor usar
explicit). \n
Exemplo: upcast correto
\nclass Base { public: virtual void speak() { cout << "Base"; } };\nclass Derived : public Base { public: void speak() override { cout << "Derived"; } };\n\nDerived d;\nBase* b = static_cast<Base*>(&d); // upcast — seguro\nb->speak(); // Exibirá "Derived" graças à função virtual\n\nErro principal #1: Slicing ao passar por valor
\nUm dos erros mais traiçoeiros é o slicing (fatiamento de objeto). Ele ocorre quando você converte um objeto de classe derivada para a classe base por valor, e não por referência ou ponteiro