Qué hacen los operadores
Un operador es un símbolo que realiza una acción sobre uno o más valores, llamados operandos. Ya has estado usando uno: el = que asigna un valor a una variable. C++ incluye un rico conjunto de operadores para matemáticas, comparación, lógica y manipulación de bits, junto con algunas aristas afiladas que hacen tropezar a los principiantes.
En la página anterior viste cómo const bloquea un valor. Los operadores son la manera en que calculas los valores que almacenas, sean constantes o no. Recorramos las familias que usarás todos los días.
Operadores aritméticos
Los cinco operadores aritméticos son +, -, *, / y % (módulo, el resto de una división):
La gran trampa vive en ese cociente. 17 / 5 imprime 3, no 3.4. Cuando ambos operandos son enteros, / realiza una división entera y descarta la parte fraccionaria: trunca hacia cero, no redondea. Si quieres una fracción real, al menos un operando debe ser de punto flotante:
El operador % solo funciona con enteros. Usar % sobre un double es un error de compilación; emplea std::fmod de <cmath> para restos de punto flotante.
Asignación y operadores compuestos
Un solo = asigna; no compara. C++ te ofrece formas compuestas que combinan una operación con la asignación para que no repitas el nombre de la variable:
Un error clásico y doloroso es escribir = donde querías == dentro de una condición. if (x = 0) asigna 0 a x y luego evalúa el resultado (que es falso), en lugar de comparar. Los compiladores modernos avisan de esto cuando activas las advertencias: mantén -Wall activado y toma la advertencia en serio.
Operadores de comparación
Los operadores de comparación hacen una pregunta de sí/no y producen un bool (true o false):
Por defecto cout imprime un bool como 1 o 0. Inserta boolalpha una vez y cambiará a las palabras true/false para el resto de ese flujo.
Una trampa sutil: no encadenes comparaciones como 1 < x < 10. Eso se interpreta como (1 < x) < 10 - la primera comparación produce un bool (0 o 1), que luego se compara con 10, así que casi siempre el resultado es true. Escribe 1 < x && x < 10 en su lugar.
Operadores lógicos y cortocircuito
&& (y), || (o) y ! (no) combinan expresiones booleanas. Los dos primeros hacen cortocircuito: la evaluación se detiene en cuanto se conoce el resultado.
Ni check("A") ni check("B") llegan a ejecutarse - eso es el cortocircuito. No es solo una optimización; es una herramienta. Puedes escribir con seguridad if (ptr != nullptr && ptr->ready) porque la parte ptr->ready solo se alcanza cuando ptr no es nulo, evitando así desreferenciar un puntero colgante.
Incremento y decremento
++ suma uno; -- resta uno. Cada uno tiene una forma prefija y otra postfija, y la diferencia importa cuando usas el resultado:
Cuando solo quieres el efecto secundario (en un bucle for, por ejemplo), prefiere ++i. Para un int simple es idéntico, pero para tipos más pesados como los iteradores, el post-incremento tiene que copiar primero el valor anterior, lo cual es trabajo desperdiciado.
Una advertencia más: no modifiques la misma variable dos veces en una sola expresión, como i = i++ + 1; o arr[i] = i++;. El orden de esas actualizaciones no está especificado y el resultado es comportamiento indefinido. Limita cada variable a una sola modificación por sentencia.
Operadores a nivel de bits y precedencia
Para trabajo de bajo nivel existen los operadores a nivel de bits: & (y), | (o), ^ (xor), ~ (no) y los desplazamientos << y >>.
Cuidado: << y >> también son los operadores de flujo en cout. Dentro de una línea de cout normalmente necesitas paréntesis alrededor de un desplazamiento de bits; de lo contrario el compilador lo lee como una inserción en el flujo.
Por último, la precedencia decide qué se evalúa primero cuando mezclas operadores. * y / enlazan más fuerte que + y -, igual que en matemáticas, así que 2 + 3 * 4 es 14. La comparación enlaza más débil que la aritmética, y los lógicos &&/|| aún más débil. En la duda, no memorices toda la tabla: añade paréntesis. (a + b) * c es más claro que confiar en que el lector recuerde las reglas.
Siguiente: conversión de tipos
Viste arriba que (double)a / b forzó una división de punto flotante a partir de un entero. Eso es una conversión (cast): convertir deliberadamente un valor de un tipo a otro. En la próxima página cubriremos las herramientas de conversión de C++, desde las promociones implícitas hasta static_cast, y cuándo cada una es segura.
Preguntas frecuentes
¿Cuáles son los operadores en C++?
C++ agrupa los operadores en aritméticos (+ - * / %), de comparación (== != < > <= >=), lógicos (&& || !), de asignación (= += -= ...), incremento/decremento (++ --) y a nivel de bits (& | ^ ~ << >>). También está el ternario ?: y algunos otros como sizeof.
¿Por qué 5 / 2 da 2 en C++?
Porque ambos operandos son int, así que / realiza una división entera y la parte fraccionaria se descarta, no se redondea. Para obtener 2.5, haz que al menos un operando sea un valor de punto flotante: 5.0 / 2 o 5 / 2.0.
¿Cuál es la diferencia entre ++i e i++ en C++?
Ambos suman 1 a i. ++i (pre-incremento) incrementa primero y devuelve el nuevo valor; i++ (post-incremento) devuelve el valor anterior y luego incrementa. Cuando solo te importa el efecto secundario, prefiere ++i.