Las macros son una herramienta que nos permite realizar sustituciones de texto antes de la compilación del código.
Las macros son una funcionalidad del preprocesador de C++ (etapa anterior a la compilación del código). El preprocesador lee el código fuente y, cuando encuentra una referencia a una macro, reemplaza el texto con el código o la expresión asociada a la macro.
Las macros son una característica heredada del lenguaje C
En C++ es frecuente abusa vilmente del uso de macros, bien por costumbre o por la idea (equivocada) de que son la alternativa más eficientes. Pero C++, casi siempre, tenemos alternativas mejores.
Evitad usarlas por todos los medios. Es más, que no uséis macros 😅
Definición de Macros
Las macros se definen utilizando la directiva #define
seguida del nombre de la macro y su reemplazo.
#define NOMBRE_MACRO sustitucion
Son las macros más simples y consisten en la sustitución de un identificador por un valor constante o una expresión.
#define PI 3.14159
#define MAX_SIZE 100
En este caso, PI
y MAX_SIZE
son macros de objetos que se reemplazarán por 3.14159
y 100
respectivamente en todo el código donde se utilicen.
#define PI 3.14159
int main() {
double radio = 5.0;
double area = PI * radio * radio;
return 0;
}
En este ejemplo,
- La macro
PI
se utiliza para calcular el área de un círculo. - Antes de la compilación, el preprocesador reemplazará
PI
por3.14159
.
También es posible definir macros que aceptan parámetros, lo que las hace similares a las funciones.
Pero con una diferencia muy importante, las macros se expanden en tiempo de compilación (en lugar de evaluarse en tiempo de ejecución)
#define SQUARE(x) ((x) * (x))
int main() {
int valor = 5;
int resultado = SQUARE(valor);
return 0;
}
Aquí, la macro SQUARE(x)
se expande a ((valor) * (valor))
, resultando en 25
.
Desdefinir macros
Podemos eliminar la definición de una macro usando #undef
. De hecho, es buena práctica eliminarla para evitar posibles conflictos en otras partes del código.
#define MAX_SIZE 100
// ... uso de MAX_SIZE ...
#undef MAX_SIZE
Ventajas y desventajas
Si antes os he dicho que evitéis usar las macros lo más posible, lógicamente es porque tienen desventajas. Vamos a hablar un poco de esto 👇
Ventajas
- Eficiencia: Como las macros se expanden antes de la compilación, no introducen sobrecarga en tiempo de ejecución.
- Código reutilizable y portable: Las macros permiten encapsular patrones de código repetitivos, o permitir moverlo a otro sistema o dispositivo.
Más o menos todas son ciertas, pero también hay una serie de desventajas
Desventajas
Errores difíciles de depurar: Debido a que las macros simplemente reemplazan texto, pueden ser un infieeeerno de deputar y de arreglar
Se saltan el tipado: Las macros no respetan las reglas de tipado de C++
Incompatibilidad con otras partes del código: Las macros dan TODO tipo de problemas al mezclarse con otras partes del código
Evaluación múltiple de expresiones: En las macros de función, los argumentos pueden evaluarse más de una vez, lo que puede conducir a comportamientos indeseados.
#define SQUARE(x) ((x) * (x)) int a = 5; int resultado = SQUARE(a++); // a++ se evalúa dos veces
En resumen, que es una práctica muy extendida, que siempre se defiende desde el punto de vista de la eficiencia (principalmente). Pero tenemos métodos prácticamente igual de eficientes, y mucho más limpios.
Prácticas recomendadas al usar macros
Si vais a usar Macros (y en algún momento las usaréis, porque no me hacéis caso 😆) al menos, tened en cuenta siempre estas prácticas
Usar paréntesis en TODO: Siempre que definas una macro de función, asegúrate de usar paréntesis alrededor de los argumentos y la expresión completa para evitar problemas con la precedencia de operadores.
#define SQUARE(x) ((x) * (x))
Evitar side effects: Evita pasar expresiones con efectos secundarios (como incrementos
++
o decrementos--
) a las macros de función.Prefiere las constantes y funciones en lugar de macros: En muchos casos, el uso de constantes (
const
) y funcionesinline
puede ser una alternativa más segura y con mejor tipado que las macros.const double PI = 3.14159; inline int Square(int x) { return x * x; }