'constancia estática' frente a '#definir'

Resuelto Patrice Bernassola asked hace 15 años • 11 respuestas

¿Es mejor utilizar static constvariables que #definepreprocesador? ¿O tal vez depende del contexto?

¿Cuáles son las ventajas/desventajas de cada método?

Patrice Bernassola avatar Oct 28 '09 20:10 Patrice Bernassola
Aceptado

Pros y contras entre #defines, consts y (lo que has olvidado) enums, según el uso:

  1. enums:

    • sólo es posible para valores enteros
    • Los problemas de conflicto entre identificadores y alcance adecuado se manejan muy bien, particularmente en clases de enumeración de C++ 11enum class X donde las enumeraciones no están ambiguas según el alcance.X::
    • fuertemente tipado, pero a un tamaño int con o sin signo lo suficientemente grande sobre el cual no tienes control en C++03 (aunque puedes especificar un campo de bits en el que deben empaquetarse si la enumeración es miembro de struct/ class/union), mientras que C++ 11 tiene por defecto intpero el programador puede configurarlo explícitamente
    • no puedo tomar la dirección; no hay ninguna, ya que los valores de enumeración se sustituyen efectivamente en línea en los puntos de uso.
    • restricciones de uso más estrictas (por ejemplo, incremento: template <typename T> void f(T t) { cout << ++t; }no se compilará, aunque puede incluir una enumeración en una clase con un constructor implícito, un operador de conversión y operadores definidos por el usuario)
    • El tipo de cada constante se toma de la enumeración adjunta, por lo que template <typename T> void f(T)se obtiene una instanciación distinta cuando se pasa el mismo valor numérico de diferentes enumeraciones, todas las cuales son distintas de cualquier f(int)instanciación real. El código objeto de cada función podría ser idéntico (ignorando las compensaciones de direcciones), pero no esperaría que un compilador/enlazador elimine las copias innecesarias, aunque puede verificar su compilador/enlazador si lo desea.
    • incluso con typeof/decltype, no puedo esperar que numeric_limits proporcione información útil sobre el conjunto de valores y combinaciones significativos (de hecho, las combinaciones "legales" ni siquiera están anotadas en el código fuente, considere enum { A = 1, B = 2 }: es A|B"legal" desde la lógica del programa ¿perspectiva?)
    • El nombre de tipo de la enumeración puede aparecer en varios lugares en RTTI , mensajes del compilador, etc. - posiblemente útil, posiblemente ofuscado
    • no se puede usar una enumeración sin que la unidad de traducción realmente vea el valor, lo que significa que las enumeraciones en las API de la biblioteca necesitan los valores expuestos en el encabezado, y makeotras herramientas de recompilación basadas en marcas de tiempo activarán la recompilación del cliente cuando se cambien (¡mal! )

  1. consts:

    • Los problemas de conflicto de identificadores/alcance adecuado se manejan muy bien
    • tipo fuerte, único y especificado por el usuario
      • puede intentar "escribir" #defineala #define S std::string("abc"), pero la constante evita la construcción repetida de temporales distintos en cada punto de uso
    • Complicaciones de la regla de una definición
    • puede tomar direcciones, crear referencias constantes a ellas, etc.
    • más similar a un sin constvalor, que minimiza el trabajo y el impacto si se cambia entre los dos
    • El valor se puede colocar dentro del archivo de implementación, lo que permite una recompilación localizada y solo enlaces de cliente para recoger el cambio.

  1. #defines:

    • alcance "global"/más propenso a usos conflictivos, lo que puede producir problemas de compilación difíciles de resolver y resultados inesperados en tiempo de ejecución en lugar de mensajes de error sensatos; mitigar esto requiere:
      • identificadores largos, oscuros y/o coordinados centralmente, y el acceso a ellos no puede beneficiarse de la coincidencia implícita de espacios de nombres usados/actuales/buscados por Koenig, alias de espacios de nombres, etc.
      • Si bien la mejor práctica permite que los identificadores de parámetros de plantilla sean letras mayúsculas de un solo carácter (posiblemente seguidas de un número), otros usos de identificadores sin letras minúsculas se reservan convencionalmente y se esperan de las definiciones del preprocesador (fuera del sistema operativo y la biblioteca C/C++). encabezados). Esto es importante para que el uso del preprocesador a escala empresarial siga siendo manejable. Se puede esperar que las bibliotecas de terceros cumplan. Observar esto implica que la migración de constantes o enumeraciones existentes hacia/desde definiciones implica un cambio en las mayúsculas y, por lo tanto, requiere ediciones en el código fuente del cliente en lugar de una recompilación "simple". (Personalmente, escribo en mayúscula la primera letra de las enumeraciones, pero no las constantes, por lo que también me vería obligado a migrar entre esas dos; tal vez sea hora de repensarlo).
    • Es posible realizar más operaciones en tiempo de compilación: concatenación literal de cadenas, encadenamiento (tomando su tamaño), concatenación en identificadores.
      • La desventaja es que, dado #define X "x"y cierto uso del cliente "pre" X "post", si desea o necesita hacer de X una variable modificable en tiempo de ejecución en lugar de una constante, fuerza las ediciones en el código del cliente (en lugar de solo la recompilación), mientras que esa transición es más fácil desde a const char*o const std::stringdado que ya obliga al usuario a incorporar operaciones de concatenación (por ejemplo, "pre" + X + "post"para string)
    • no se puede usar sizeofdirectamente en un literal numérico definido
    • sin tipo (GCC no advierte si se compara con unsigned)
    • Es posible que algunas cadenas de compilador/enlazador/depurador no presenten el identificador, por lo que se verá reducido a mirar "números mágicos" (cadenas, lo que sea...)
    • no puedo tomar la dirección
    • el valor sustituido no necesita ser legal (o discreto) en el contexto donde se crea #define, ya que se evalúa en cada punto de uso, por lo que puede hacer referencia a objetos aún no declarados, depende de la "implementación" que no necesita estar preincluido, crear "constantes" que { 1, 2 }puedan usarse para inicializar matrices, etc. #define MICROSECONDS *1E-6definitivamente no recomiendo esto!)
    • algunas cosas especiales como __FILE__y __LINE__se pueden incorporar a la macro sustitución
    • puede probar la existencia y el valor en #ifdeclaraciones para incluir código condicionalmente (más poderoso que un "if" posterior al preprocesamiento, ya que el código no necesita ser compilable si no lo selecciona el preprocesador), usar #undef-ine, redefinir, etc.
    • El texto sustituido debe estar expuesto:
      • en la unidad de traducción que la utiliza, lo que significa que las macros en las bibliotecas para uso del cliente deben estar en el encabezado, por lo que makeotras herramientas de recompilación basadas en marcas de tiempo activarán la recompilación del cliente cuando se cambien (¡mal!).
      • o en la línea de comando, donde se necesita aún más cuidado para garantizar que el código del cliente se vuelva a compilar (por ejemplo, el Makefile o script que proporciona la definición debe aparecer como una dependencia)

Mi opinión personal:

Como regla general, uso consts y los considero la opción más profesional para uso general (aunque los demás tienen una simplicidad que atrae a este viejo programador vago).

Tony Delroy avatar Oct 01 '2010 00:10 Tony Delroy

Personalmente, detesto el preprocesador, así que siempre elegiría const.

La principal ventaja de a #definees que no requiere memoria para almacenarse en su programa, ya que en realidad solo reemplaza algo de texto con un valor literal. También tiene la ventaja de que no tiene tipo, por lo que puede usarse para cualquier valor entero sin generar advertencias.

Las ventajas de " const"s son que se les puede establecer un alcance y se pueden usar en situaciones en las que es necesario pasar un puntero a un objeto.

Sin embargo , no sé exactamente a qué te refieres con la staticparte " ". Si declaras globalmente, lo pondría en un espacio de nombres anónimo en lugar de usar static. Por ejemplo

namespace {
   unsigned const seconds_per_minute = 60;
};

int main (int argc; char *argv[]) {
...
}
T.E.D. avatar Oct 28 '2009 13:10 T.E.D.

Si se trata de una pregunta de C++ y se menciona #definecomo alternativa, entonces se trata de constantes "globales" (es decir, de alcance de archivo), no de miembros de la clase. Cuando se trata de este tipo de constantes en C++ static constes redundante. En C++ consttienen enlaces internos de forma predeterminada y no tiene sentido declararlos static. Así que en realidad se trata constde vs.#define

Y, por último, constes preferible en C++. Al menos porque dichas constantes están tipificadas y tienen alcance. Simplemente no hay razones para preferirlo #define, constsalvo algunas excepciones.

Las constantes de cadena, por cierto, son un ejemplo de tal excepción. Con #definelas constantes de cadena d se puede utilizar la función de concatenación en tiempo de compilación de los compiladores C/C++, como en

#define OUT_NAME "output"
#define LOG_EXT ".log"
#define TEXT_EXT ".txt"

const char *const log_file_name = OUT_NAME LOG_EXT;
const char *const text_file_name = OUT_NAME TEXT_EXT;

PD Nuevamente, por si acaso, cuando alguien menciona static constcomo alternativa a #define, generalmente significa que está hablando de C, no de C++. Me pregunto si esta pregunta está etiquetada correctamente...

AnT stands with Russia avatar Oct 28 '2009 13:10 AnT stands with Russia

#definepuede conducir a resultados inesperados:

#include <iostream>

#define x 500
#define y x + 5

int z = y * 2;

int main()
{
    std::cout << "y is " << y;
    std::cout << "\nz is " << z;
}

Genera un resultado incorrecto:

y is 505
z is 510

Sin embargo, si reemplazas esto con constantes:

#include <iostream>

const int x = 500;
const int y = x + 5;

int z = y * 2;

int main()
{
    std::cout << "y is " << y;
    std::cout << "\nz is " << z;
}

Genera el resultado correcto:

y is 505
z is 1010

Esto se debe a que #definesimplemente reemplaza el texto. Debido a que hacer esto puede alterar seriamente el orden de las operaciones, recomendaría usar una variable constante en su lugar.

 avatar Mar 24 '2019 17:03