2009-10-28 11 views
159

¿Es mejor usar static const vars que #define preprocesador? ¿O tal vez depende del contexto?estática const vs #define

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

+12

Scott Meyers cubre este tema muy bien ya fondo. Su artículo n. ° 2 en "Eficaz tercera edición de C++". Se prefieren dos casos especiales (1) const estático dentro de un alcance de clase para constantes específicas de clase; (2) espacio de nombres o const de alcance anónimo con preferencia sobre #define. – Eric

+2

Prefiero Enumerar. Porque es híbrido de ambos. No ocupa espacio a menos que crees una variable del mismo. Si solo quieres usar como constante, enum es la mejor opción. Tiene seguridad de tipo en C/C++ 11 std y también una constante perfecta. #define es tipo inseguro, const toma espacio si el compilador no puede optimizarlo. – siddhusingh

+0

Mi decisión de usar '# define' o' static const' (para cadenas) es manejada por el aspecto ** initialization ** (no se mencionó a través de las respuestas a continuación): si se usa constant solo en una unidad de compilación particular, entonces Voy con 'static const', de lo contrario utilizo' # define' - evito la inicialización de orden estática ** fiasco ** https://isocpp.org/wiki/faq/ctors#static-init-order –

Respuesta

107

Personalmente, detesto el preprocesador, por lo que siempre iría con const.

La principal ventaja de un #define es que no requiere memoria para almacenar en su programa, ya que simplemente está reemplazando algún texto con un valor literal. También tiene la ventaja de que no tiene ningún tipo, por lo que se puede usar para cualquier valor entero sin generar advertencias.

Las ventajas de "const" s son que pueden ser delimitadas, y se pueden usar en situaciones donde se necesita pasar un puntero a un objeto.

Sin embargo, no sé exactamente qué es lo que está consiguiendo con la parte "estática". Si declaras globalmente, lo pondría en un espacio de nombre anónimo en lugar de usar estática. Por ejemplo

namespace { 
    unsigned const seconds_per_minute = 60; 
}; 

int main (int argc; char *argv[]) { 
... 
} 
+0

Eso es lo que quiero saber. En mi compañía, la estrategia es poner todas las cadenas constantes como #define en un archivo separado. Estaba pensando que puede ser mejor usar const vars en su lugar. Gracias –

+7

* Las constantes de String * específicamente son una de las que podrían beneficiarse de ser '# define'd, al menos si se pueden usar como" bloques de construcción "para las constantes de cadena más grandes.Ver mi respuesta para un ejemplo. – AnT

+0

Un caso que no había considerado. Gracias por tu tiempo. –

5

Usar una constante estática es como usar cualquier otra variable constante en su código. Esto significa que puede rastrear desde donde proviene la información, a diferencia de un #define que simplemente se reemplazará en el código en el proceso de precompilación.

Es posible que desee echar un vistazo a la C++ FAQ Lite para esta pregunta: http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.7

36

Si se trata de una cuestión de C++ y menciona #define como alternativa, a continuación, se trata de constantes (es decir, el archivo de alcance mundial) "", no sobre miembros de la clase. Cuando se trata de tales constantes en C++ static const es redundante. En C++ const tienen un enlace interno por defecto y no tiene sentido declararlos static. Entonces realmente se trata de const contra #define.

Y, finalmente, en C++ const es preferible. Al menos porque tales constantes son mecanografiadas y delimitadas. Simplemente no hay razones para preferir #define sobre const, salvo algunas excepciones.

Las constantes de cadena, por cierto, son un ejemplo de esta excepción. Con #define constantes d de cadena se puede utilizar en tiempo de compilación característica concatenación de los compiladores de 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; 

P.S. De nuevo, por si acaso, cuando alguien menciona static const como alternativa al #define, generalmente significa que están hablando de C, no de C++. Me pregunto si esta pregunta se etiqueta correctamente ...

4
  • Un static const se escribe (que tiene un tipo) y se puede comprobar por el compilador para su validez, etc. redefinición
  • un #define puede Redifined indefinido lo que sea.

Por lo general, debería preferir las constelaciones estáticas.No tiene ninguna desventaja. El procesador debe usarse principalmente para la compilación condicional (y, a veces, para trucos realmente sucios).

194

Pros y contras de todo, dependiendo del uso:

  • enumeraciones
    • sólo es posible para los valores enteros
    • problemas de choque/identificador de ámbito adecuadamente manejados muy bien, sobre todo en 11 clases de enumeración C++ donde las enumeraciones para enum class X son desambiguadas por el alcance X::
    • fuertemente tipeado, pero para un tamaño int suficientemente grande, firmado o no, sobre el que no tiene control en C++ 03 (aunque puede especificar un campo de bit en el que deben empaquetarse si enum es miembro de struct/class/union), mientras que C++ 11 se establece de manera predeterminada en int pero puede establecerlo explícitamente el programador
    • no se puede tomar la dirección; no hay una, ya que los valores de enumeración se sustituyen efectivamente en línea en los puntos de uso
    • restricciones de uso más fuertes (p. ej. incrementando - template <typename T> void f(T t) { cout << ++t; } no se compilará, aunque se puede envolver una enumeración en una clase con el constructor implícito, fundición operador y operadores definidos por el usuario)
    • tipo de cada constante toma de la enumeración que encierra, por lo template <typename T> void f(T) obtener una ejemplificación distinta cuando se pasa el mismo valor numérico de diferentes enumeraciones, todas las cuales son distintas de cualquier instanciación real de f(int). El código objeto de cada función podría ser idéntico (ignorando los desplazamientos de dirección), pero no esperaría que un compilador/enlazador elimine las copias innecesarias, aunque podría verificar su compilador/vinculador si lo desea.
    • incluso con typeof/decltype, no se puede esperar que los numeric_limits proporcionen información útil sobre el conjunto de valores significativos y combinaciones (de hecho, las combinaciones "legales" ni siquiera se anotan en el código fuente, considere enum { A = 1, B = 2 } - es A|B "legal "¿desde una perspectiva lógica de programa?)
    • el nombre de tipo de la enumeración puede aparecer en varios lugares en RTTI, mensajes del compilador, etc. posiblemente útil, posiblemente ofuscación
    • no 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 make y otras herramientas de recompilación basadas en la marca de tiempo desencadenarán la recompilación del cliente cuando se cambien (¡mal!)
  • consts
    • problemas de choque/identificador adecuadamente con ámbito manejan muy bien
    • fuerte, el tipo especificado por el usuario, única
      • usted puede tratar de "tipo" de un #define ala #define S std::string("abc"), pero las evita constantes construcción repetida de temporales distintos en cada punto de uso
    • Una definición Regla complicaciones
    • puede tomar la dirección, crear const referencias a ellos, etc.
    • más parecido a un valor no- const, lo que minimiza el trabajo y el impacto si el cambio entre el valor de dos
    • puede ser colocado en el interior del archivo de aplicación, lo que permite una recompilación localizada y enlaces cliente acaba de recoger el cambio
  • define
    • alcance "global"/más propensos a usos conflictivos, que pueden producir problemas de compilación difíciles de resolver y resultados inesperados en tiempo de ejecución en lugar de mensajes de error sanos; mitigar esto requiere:
      • identificadores largos, oscuros y/o coordinadas centralmente, y el acceso a ellos no puede beneficiarse de forma implícita encontrada de uso/espacio de nombres/Koenig-mirado corriente arriba, alias de espacio de nombres, etc.
      • mientras que el Truncado las mejores prácticas permiten que los identificadores de parámetros de la plantilla sean letras mayúsculas de un solo carácter (posiblemente seguidas por un número), otros usos de identificadores sin letras minúsculas se reservan y esperan para el preprocesador (fuera de los encabezados de las bibliotecas OS y C/C++). Esto es importante para que el uso del preprocesador a escala empresarial siga siendo manejable. Se espera que las bibliotecas de terceros cumplan. Al observar esto, la migración de consts o enumeraciones existentes desde/hacia define implica un cambio en las mayúsculas y, por lo tanto, requiere modificaciones en el código fuente del cliente en lugar de una recompilación "simple". (Personalmente, en mayúscula la primera letra de las enumeraciones, pero no consts, por lo que estaría golpeó la migración entre los dos también - tal vez el momento de replantearse eso.)
      • operaciones
    • más posible en tiempo de compilación: concatenación de cadenas literales, stringification (teniendo el tamaño de los mismos), la concatenación en identificadores
      • desventaja es que dado #define X "x" y algo de uso del cliente ala "pre" X "post", si desea o necesita hacer X una variable de tiempo de ejecución modificables en lugar de una constante se fuerza ediciones en código de cliente (en lugar de simplemente recompilar), mientras que esa transición es más fácil desde un const char* o const std::string dado que ya fuerzan al usuario a incorporar el concat operaciones de enación (p. "pre" + X + "post" para string)
    • no pueden utilizar sizeof directamente en una definida numérico literal
    • sin tipo (GCC no advierte si se compara con unsigned)
    • algunas cadenas compilador/enlazador/depurador puede no presentar el identificador, por lo que puede reducir a mirar "números mágicos" (cadenas, lo que sea ...)
    • no puede tomar la dirección de
    • el valor sustituido no tiene que ser legal (o discreta) 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, depender de la "implementación" que no debe preincluirse, crear "constantes" como { 1, 2 } que se pueden utilizar para inicializar matrices, o #define MICROSECONDS *1E-6, etc. ( definitivamente no recomendar este!)
    • algunas cosas especiales como __FILE__ y __LINE__ se pueden incorporar en la sustitución de macro
    • se puede probar la existencia y el valor de #if declaraciones para la inclusión de código de forma condicional (más potente que un post- preprocesamiento "si" ya que el código no necesita ser compilable si no es seleccionado por el preprocesador), use #undef -ine, redefina, etc.
    • texto sustituido tiene que estar expuesto:
      • en la unidad de traducción es utilizado por, lo que significa que las macros en las bibliotecas para uso del cliente debe estar en la cabecera, por lo make y otras herramientas de recompilación basada en fecha y hora se activará recompilación cliente cuando que se cambia (mal)
      • o en la línea de comandos, donde se necesita aún más cuidado para asegurarse de código de cliente se vuelve a compilar (por ejemplo, el Makefile o script suministro de la definición debe aparecer como una dependencia)

Como regla general, uso const sy los considero la opción más profesional para el uso general (aunque los otros tienen una simplicidad que atrae a este viejo programador perezoso). No se recomienda

+17

+1: aprecia su resumen – Chubsdad

+1

Awesome answer. Una pequeña queja: a veces uso enumeraciones locales que no están en los encabezados en absoluto solo por la claridad del código, como en las máquinas de estado pequeño y demás. Por lo tanto, no tienen que estar en los encabezados, en todo momento. – kert

+0

@kert: punto sólido ... editado en consecuencia. Gracias. –

1

constantes que definen mediante el uso de directiva de preprocesador #define aplicar no sólo en C++, sino también en C. Estas constantes no tendrán el tipo. Incluso en C se propuso utilizar const para las constantes.

0

Si está definiendo una constante para compartir entre todas las instancias de la clase, use const estático. Si la constante es específica para cada instancia, simplemente use const (pero tenga en cuenta que todos los constructores de la clase deben inicializar esta variable miembro miembro en la lista de inicialización).

0

Siempre prefiera usar las funciones de idioma sobre algunas herramientas adicionales como el preprocesador.

ES.31: No usar macros para las constantes o "funciones"

macros son una fuente importante de errores. Las macros no obedecen el alcance habitual y las reglas de tipo. Las macros no obedecen las reglas habituales para el argumento que pasa. Las macros garantizan que el lector humano vea algo diferente según lo que ve el compilador. Las macros complican la construcción de herramientas.

De C++ Core Guidelines