O que os operadores fazem
Um operador é um símbolo que realiza uma ação sobre um ou mais valores, chamados operandos. Você já vem usando um: o = que liga um valor a uma variável. C++ vem com um conjunto rico de operadores para matemática, comparação, lógica e manipulação de bits - e algumas arestas afiadas que pegam os iniciantes de surpresa.
Na página anterior você viu como const trava um valor. Os operadores são a forma de calcular os valores que você armazena, sejam eles constantes ou não. Vamos percorrer as famílias que você usará todos os dias.
Operadores aritméticos
Os cinco operadores aritméticos são +, -, *, / e % (módulo, o resto de uma divisão):
A grande armadilha vive nesse quociente. 17 / 5 imprime 3, não 3.4. Quando ambos os operandos são inteiros, / faz divisão inteira e descarta a parte fracionária - ele trunca em direção a zero, não arredonda. Se você quer uma fração real, pelo menos um operando precisa ser de ponto flutuante:
O operador % só funciona com inteiros. Usar % em um double é um erro de compilação - use std::fmod de <cmath> para restos de ponto flutuante.
Atribuição e operadores compostos
Um único = atribui; ele não compara. C++ oferece formas compostas que combinam uma operação com a atribuição para que você não precise repetir o nome da variável:
Um erro clássico e doloroso é escrever = onde você queria == dentro de uma condição. if (x = 0) atribui 0 a x e então testa o resultado (que é falso), em vez de comparar. Compiladores modernos avisam sobre isso quando você ativa os avisos - mantenha -Wall ligado e leve o aviso a sério.
Operadores de comparação
Os operadores de comparação fazem uma pergunta de sim/não e produzem um bool (true ou false):
Por padrão, cout imprime um bool como 1 ou 0. Insira boolalpha uma vez e ele passa a mostrar as palavras true/false no restante daquele fluxo.
Uma armadilha sutil: não encadeie comparações como 1 < x < 10. Isso é interpretado como (1 < x) < 10 - a primeira comparação produz um bool (0 ou 1), que é então comparado com 10, de modo que o resultado é quase sempre true. Escreva 1 < x && x < 10 em vez disso.
Operadores lógicos e curto-circuito
&& (e), || (ou) e ! (não) combinam expressões booleanas. Os dois primeiros fazem curto-circuito: a avaliação para assim que o resultado é conhecido.
Nem check("A") nem check("B") chegam a rodar - isso é o curto-circuito. Não é apenas uma otimização; é uma ferramenta. Você pode escrever com segurança if (ptr != nullptr && ptr->ready) porque a parte ptr->ready só é alcançada quando ptr não é nulo, evitando a desreferência de um ponteiro pendente.
Incremento e decremento
++ soma um; -- subtrai um. Cada um tem uma forma prefixa e uma posfixa, e a diferença importa quando você usa o resultado:
Quando você quer apenas o efeito colateral (em um laço for, por exemplo), prefira ++i. Para um int simples é idêntico, mas para tipos mais pesados como iteradores, o pós-incremento precisa copiar o valor antigo primeiro, o que é trabalho desperdiçado.
Mais um aviso: não modifique a mesma variável duas vezes em uma única expressão, como i = i++ + 1; ou arr[i] = i++;. A ordem dessas atualizações não é especificada e o resultado é comportamento indefinido. Mantenha cada variável com uma única modificação por instrução.
Operadores bit a bit e precedência
Para trabalho de baixo nível há os operadores bit a bit: & (e), | (ou), ^ (xor), ~ (não) e os deslocamentos << e >>.
Atenção: << e >> também são os operadores de fluxo em cout. Dentro de uma linha de cout, normalmente você precisa de parênteses ao redor de um deslocamento de bits, caso contrário o compilador o interpreta como uma inserção no fluxo.
Por fim, a precedência decide o que é avaliado primeiro quando você mistura operadores. * e / ligam mais forte do que + e -, assim como na matemática, então 2 + 3 * 4 é 14. A comparação liga mais fraco que a aritmética, e os lógicos &&/|| ainda mais fraco. Na dúvida, não memorize a tabela inteira - adicione parênteses. (a + b) * c é mais claro do que confiar que o leitor lembre das regras.
Próximo: conversão de tipos
Você viu acima que (double)a / b forçou um inteiro a entrar em uma divisão de ponto flutuante. Isso é uma conversão (cast) - converter deliberadamente um valor de um tipo para outro. Na próxima página vamos cobrir as ferramentas de conversão de C++, das promoções implícitas ao static_cast, e quando cada uma é segura.
Perguntas frequentes
Quais são os operadores em C++?
C++ agrupa os operadores em aritméticos (+ - * / %), de comparação (== != < > <= >=), lógicos (&& || !), de atribuição (= += -= ...), incremento/decremento (++ --) e bit a bit (& | ^ ~ << >>). Há também o ternário ?: e alguns outros, como sizeof.
Por que 5 / 2 é igual a 2 em C++?
Porque ambos os operandos são int, então / faz divisão inteira e a parte fracionária é descartada - não arredondada. Para obter 2.5, faça com que pelo menos um operando seja um valor de ponto flutuante: 5.0 / 2 ou 5 / 2.0.
Qual é a diferença entre ++i e i++ em C++?
Ambos somam 1 a i. ++i (pré-incremento) incrementa primeiro e devolve o novo valor; i++ (pós-incremento) devolve o valor antigo e depois incrementa. Quando você só se importa com o efeito colateral, prefira ++i.