const estática vs # definir


Es mejor usar static const vars que #define preprocesador? O tal vez, depende del contexto?

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

 172
c++
Author: T.E.D., 2009-10-28

9 answers

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

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

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

Sin embargo, no se exactamente a lo que estás llegando con la parte "estática". Si está declarando globalmente, lo pondría en un espacio de nombres anonómico en lugar de usar estático. Por ejemplo

namespace {
   unsigned const seconds_per_minute = 60;
};

int main (int argc; char *argv[]) {
...
}
 117
Author: T.E.D.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-28 13:49:49

Pros y contras de todo, dependiendo del uso:

  • enumeraciones
    • solo es posible para valores enteros
    • correctamente scoped / identifier clash los problemas se manejan bien, particularmente en las clases de enumeración de C++11 donde las enumeraciones de enum class X están desambiguadas por el ámbito X::
    • fuertemente escrito, pero a un tamaño int lo suficientemente grande con o sin signo sobre el que no tiene control en C++03 (aunque puede especificar un campo de bits en el que se deben empaquetar si el enum es un miembro de struct / class / union), mientras que C++11 por defecto es int pero puede ser establecido explícitamente por el programador
    • no 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 (por ejemplo, incrementar - template <typename T> void f(T t) { cout << ++t; } no compilará, aunque puede envolver una enumeración en una clase con constructor implícito, operador de casting y operadores definidos por el usuario)
    • el tipo de cada constante tomado de la encerrando enum, entonces template <typename T> void f(T) obtiene una instanciación distinta cuando se pasa el mismo valor numérico de enum diferentes, todos los cuales son distintos de cualquier instanciación real f(int). 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 eliminara las copias innecesarias, aunque podría verificar su compilador/enlazador si le importa.
    • incluso con typeof/decltype, no se puede esperar que numeric_limits proporcione información útil sobre el conjunto de valores y combinaciones significativas (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 perspectiva de la lógica del programa?)
    • el nombre del 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 vea realmente el valor, lo que significa que las enumeraciones en las API de la biblioteca necesitan los valores expuestos en el encabezado, y make y otros las herramientas de recompilación basadas en marcas de tiempo activarán la recompilación del cliente cuando se cambien (¡mal!)
  • consts
    • correctamente scoped / identifier clash issues handled nicely
    • tipo fuerte, único, especificado por el usuario
      • puede intentar "escribir" a #define ala #define S std::string("abc"), pero la constante evita la construcción repetida de temporarios distintos en cada punto de uso
    • Una Regla de Definición complicaciones
    • puede tomar dirección, crear const referencias a ellos, etc.
    • más similar a un valor no-const, 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
  • define
    • ámbito "global" / más propenso 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 cuerdos; 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/current/Koenig-looked-up, alias de espacios de nombres, etc.
      • mientras que la mejor práctica de trumping permite que los identificadores de parámetros de plantilla sean letras mayúsculas de un solo carácter (posiblemente seguidas de un número), otro uso de identificadores sin letras minúsculas se reserva convencionalmente y se espera de preprocesador define (fuera de las cabeceras de la biblioteca OS y C/C++). 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 la migración de conss o enums existentes a / desde defines implica un cambio en la capitalización, y por lo tanto requiere ediciones en el código fuente del cliente en lugar de una recompilación "simple". (Personalmente, pongo en mayúscula la primera letra de las enumeraciones, pero no consts, así que me golpearía migrando entre esos dos también-tal vez es hora de repensar eso.)
    • más operaciones en tiempo de compilación posibles: concatenación literal de cadenas, stringificación (tomando su tamaño), concatenación en identificadores
      • el inconveniente es que dado #define X "x" y algún uso del cliente ala "pre" X "post", si desea o necesita hacer de X una variable de tiempo de ejecución en lugar de una constante, fuerza las ediciones al código del cliente (en lugar de solo recompilación), mientras que esa transición es más fácil desde un const char* o const std::string dado que ya fuerzan el usuario para incorporar operaciones de concatenación (por ejemplo, "pre" + X + "post" para string)
    • no se puede usar sizeof directamente en un literal numérico definido
    • sin escribir (GCC no advierte si se compara con unsigned)
    • algunas cadenas de compilador/enlazador/depurador pueden no presentar el identificador, por lo que se verá reducido a mirar "números mágicos" (cadenas, lo que sea...)
    • no puede tomar la dirección
    • el valor sustituido no tiene por qué ser legal (o discreto) en el contexto # define se crea, ya que se evalúa en cada punto de uso, por lo que puede hacer referencia a objetos aún no declarados, depender de "implementación" que no necesita preincluirse, crear "constantes" como { 1, 2 } que se pueden usar para inicializar matrices, o #define MICROSECONDS *1E-6, etc. ( definitivamente ¡no recomiendo esto!)
    • algunas cosas especiales como __FILE__ y __LINE__ se pueden incorporar en la macro sustitución
    • puede probar la existencia y el valor en las declaraciones #if para condicionalmente incluyendo código (más poderoso que un post-preprocesamiento "if" ya que el código no necesita ser compilable si no es seleccionado por el preprocesador), use #undef-ine, redefine, etc.
    • el texto sustituido debe exponerse:
      • en la unidad de traducción que se utiliza, lo que significa que las macros en las bibliotecas para uso del cliente deben estar en el encabezado, por lo que make y otras 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 comandos, donde incluso se necesita más cuidado para asegurarse de que el código del cliente se recompila (por ejemplo, el Makefile o el script que proporciona la definición debe aparecer como una dependencia)

Como regla general, utilizo const s y los considero la opción más profesional para el uso general (aunque los otros tienen una simplicidad atractiva para este viejo programador perezoso).

 204
Author: Tony D,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-10 01:28:00

Si esta es una pregunta de C++ y menciona #define como una alternativa, entonces se trata de constantes "globales" (es decir, de ámbito de archivo), no de miembros de clase. Cuando se trata de tales constantes en C++ static const es redundante. En C++ const tienen enlace interno por defecto y no tiene sentido declararlos static. Así que realmente se trata de const vs. #define.

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

Las constantes de cadena, por cierto, son un ejemplo de tal excepción. Con #define constantes de cadena d se puede usar la función de concatenación en tiempo de compilación de 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.d. De nuevo, por si acaso, cuando alguien menciona static const como una alternativa a #define, generalmente significa que están hablando de C, no de C++. Me pregunto si esta pregunta está debidamente etiquetada...

 40
Author: AnT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-28 14:03:23

Usar una const estática es como usar cualquier otra variable const en su código. Esto significa que puede rastrear de dónde viene la información, en lugar de una #define que simplemente se reemplazará en el código en el proceso de pre-compilació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

 5
Author: JP.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-28 13:48:14
  • Se escribe una const estática (tiene un tipo) y el compilador puede comprobar su validez, redefinición, etc.
  • un #define puede ser redifinado indefinido lo que sea.

Por lo general, usted debe preferir los componentes estáticos. No tiene ninguna desventaja. El prprocessor debe usarse principalmente para compilación condicional (y a veces para trics realmente sucios tal vez).

 4
Author: RED SOFT ADAIR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-28 13:55:07

Por favor vea aquí: const estática vs definir

Por lo general, una declaración const (tenga en cuenta que no necesita ser estática) es el camino a seguir

 1
Author: ennuikiller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-28 13:48:21

No se recomienda definir constantes usando la directiva preprocesador #define para aplicarlas no solo en C++, sino también en C. Estas constantes no tendrán el tipo. Incluso en C se propuso usar const para constantes.

 1
Author: Aleksey Bykov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-23 09:50:03

Si está definiendo una constante que se compartirá entre todas las instancias de la clase, utilice static const. 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 const en la lista de inicialización).

 0
Author: snr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-07 18:40:10

Siempre prefieren usar las características del lenguaje sobre algunas herramientas adicionales como preprocesador.

ES.31: No utilice macros para constantes o "funciones"

Las macros son una fuente importante de errores. Las macros no obedecen al ámbito habitual y escribir reglas. Las macros no obedecen las reglas habituales para argumentar pasar. Las macros aseguran que el lector humano vea algo diferente de lo que ve el compilador. Las macros complican la construcción de herramientas.

Desde C++ Directrices Básicas

 0
Author: Hitokage,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-25 17:20:36