2012-04-21 20 views
8

He estado desarrollando C++ durante menos de un año, pero en ese momento, he escuchado a varias personas hablar sobre lo horrible que es #define. Ahora, me doy cuenta de que es interpretado por el preprocesador en lugar del compilador, y por lo tanto, no se puede depurar, pero, ¿es esto realmente tan malo?Alternativas al uso de "#define" en C++? ¿Por qué está mal visto?

Aquí es un ejemplo (código no probado, pero se entiende la idea general):

#define VERSION "1.2" 

#include <string> 

class Foo { 
    public: 
    string getVersion() {return "The current version is "+VERSION;} 
}; 
  1. ¿Por qué es este código malo?
  2. ¿Existe alguna alternativa al uso de #define?
+2

El inverso de esta pregunta: [Por qué usar #define en lugar de una variable] (http://stackoverflow.com/questions/6004963/why-use-define-instead-of-a-variable) –

+0

'define' s son construcciones complicadas y pueden ser fácilmente mal usadas o abusadas. pero al igual que 'goto' pueden ser muy útiles. – Anycorn

+1

posible duplicado de [static const vs #define] (http://stackoverflow.com/questions/1637332/static-const-vs-define), en particular esta [respuesta] (http://stackoverflow.com/a/ 3835772/61574) debería ser suficiente. – Anonymous

Respuesta

8

¿Por qué este código es malo?

Porque se puede sobrescribir VERSION y el compilador no le dirá nada.

¿Existe alguna alternativa al uso de #define?

const char * VERSION = "1.2"; 

o

const std::string VERSION = "1.2"; 
+3

En realidad, es probable que el compilador * le diga * cuándo redefine una macro. Incluso sin ninguna opción de advertencia, gcc lo advertirá con "advertencia:" FOO "redefinido ... nota: esta es la ubicación de la definición anterior". Y lo hará solo si los valores son diferentes, para que no reciba advertencias inútiles. –

+0

¿Tiene una const el mismo alcance que un '# define'? Supongo que un '# define' realmente no tiene un alcance, puede ir a cualquier parte. ¿Puede una const ser utilizada en cualquier lugar también? – Joel

+3

@Joel: una de las mayores ventajas de 'const x' sobre' # define' es que tiene un alcance. – Puppy

4

#define no es intrínsecamente mala, es sólo fácil de abuso. Para algo así como una cadena de versión funciona bien, aunque un const char* sería mejor, pero muchos programadores lo usan para mucho más que eso. Usar #define como typedef por ejemplo es una tontería cuando, en la mayoría de los casos, un typedef sería mejor. Por lo tanto, no hay nada de malo en las declaraciones #define, y algunas cosas no se pueden hacer sin ellas. Deben evaluarse caso por caso. Si puede encontrar una manera de resolver un problema sin usar el preprocesador, debe hacerlo.

0

En general, el preprocesador es malo porque crea un proceso de compilación de dos pasos que no es seguro, crea mensajes de error difíciles de decodificar y puede dar lugar a código difícil de leer. Usted no debe usar, si es posible:

const char* VERSION = "1.2" 

Sin embargo, hay casos en los que es imposible hacer lo que quiere hacer sin el preprocesador:

#define Log(x) cout << #x << " = " << (x) << endl; 
+0

La compilación en C++ en realidad lleva más de dos pases. –

+0

Me refiero a pases lógicos. –

+1

No sé a qué te refieres con "pases lógicos". O más bien, no veo cómo eso sería intrínsecamente inseguro. –

10

El verdadero problema es que define son manejados por una herramienta diferente del resto del lenguaje (el preprocesador). Como consecuencia, el compilador no lo conoce y no puede ayudarlo cuando algo sale mal, como la reutilización de un nombre de preprocesador.

Considere el caso de max que a veces se implementa como una macro. Como consecuencia, no puede usar el identificador max en ningún lugar de su código. En cualquier parte. Pero el compilador no te lo dirá. En cambio, su código irá terriblemente mal y no tiene idea de por qué.

Ahora, con cierto cuidado este problema se puede minimizar (si no se elimina por completo). Pero para la mayoría de los usos de #define hay mejores alternativas de todos modos, por lo que el cálculo del costo/beneficio se ve sesgado: leve desventaja para no beneficio alguno. ¿Por qué usar una característica defectuosa cuando no ofrece ninguna ventaja?

Así que aquí es un esquema muy simple:

  1. Necesita una constante? Use una constante (no es una definición)
  2. ¿Necesita una función? Use una función (no una definición)
  3. ¿Necesita algo que no se pueda modelar utilizando una constante o una función? Use un definir, pero hágalo correctamente.

Hacerlo “adecuada” es un arte en sí mismo, pero hay algunas pautas sencillas:

  1. utilizar un nombre único. Todas las mayúsculas, siempre precedidas por un identificador de biblioteca único. max? Fuera. VERSION? Fuera. En su lugar, use MY_COOL_LIBRARY_MAX y MY_COOL_LIBRARY_VERSION. Por ejemplo, las bibliotecas de Boost, grandes usuarios de macros, siempre usan macros comenzando con BOOST_<LIBRARY_NAME>_.

  2. Tenga cuidado con la evaluación. En efecto, un parámetro en una macro es solo texto que se reemplaza. Como consecuencia, #define MY_LIB_MULTIPLY(x) x * x está roto: podría usarse como MY_LIB_MULTIPLY(2 + 5), lo que da como resultado 2 + 5 * 2 + 5. No es lo que queríamos Para protegerse de esto, siempre parenhesise usos de los argumentos (a menos que sepa exactamente lo que está haciendo - spoiler: probablemente no, incluso los expertos lo hacen de forma alarmante a menudo).

    La versión correcta de esta macro sería:

    #define MY_LIB_MULTIPLY(x) ((x) * (x)) 
    

Pero hay todavía un montón de maneras de conseguir macros muy mal, y, para reiterar, el compilador no le ayudará aquí.

2

yo no usaría #define para definir una constante palabra clave use static o mejor aún const int kMajorVer = 1; const int kMinorVer = 2; O const std::string kVersion = "1.2";

Herb Sutter tiene un excelente artículo aquí que detalla qué #define es malo y enumera algunos ejemplos donde hay realmente no hay otra manera de lograr lo mismo: http://www.gotw.ca/gotw/032.htm.

Básicamente, con muchas cosas está bien siempre y cuando lo use correctamente, pero es fácil de abusar y los errores de macro son particularmente crípticos y un error para depurar.

Personalmente los uso para el código de depuración condicional y también representaciones de datos variantes, que se detalla al final del artículo de sutter.

Cuestiones relacionadas