El lenguaje de programación C++: Declaraciones y One Definition Rule

El capítulo 4 del libro El Lenguaje de programación C++ versa sobre los tipos y declaraciones. Como comentaba ayer, ya en este capítulo se presentan algunos problemas al explicarnos características típicas de C++ como la conocida One Definition Rule o Regla de la Definición Única que resulta de especial interés para los más novatos y que al entenderla como se debería ahorra muchos dolores de cabeza derivados de la compilación separada de módulos.

Declaraciones y la One Definition Rule

Declarar no es definir y definir no es asignar. Son conceptos distintos que, muy a menudo, van ligados pero que debemos identificar como cosas distintas:

  • Declarar una variable es indicar que un nombre representará un pedazo de memoria para guardar un tipo determinado de cosas. Es algo así como decirle al compilador «¡ey, mira! el nombre contador guardará valores enteros».
  • Por otro lado, definir una variable es reservar memoria para ella. Es el hecho de etiquetar una porción de memoria bajo el nombre de la variable. En los lenguajes como C o C++, la definición se sirve de una previa y obligatoria declaración para reservar un espacio de memoria de un tamaño concreto.
  • Por último, asignar un valor a una variable es cambiar el contenido de la memoria que representa. Algunos lenguajes modernos como Python o Javascript, que no requieren declaraciones o definiciones de las variables reservan la memoria en la primera asignación. Los lenguajes que sí hacen uso de declaraciones, utilizan la información de la declaración para comprobar el tipo de lo que se pretende asignar a la variable. Si el tipo del contenido no coincida con el tipo de la variable se produce un «error de tipos».

Dicho esto, la One Definition Rule (ODR a partir de ahora) dice que una variable puede declararse múltiples veces pero definirse sólo una vez. Ahora bien, en el libro de referencia por excelencia del lenguaje C, El lenguaje de Programación C de Kernighan y Ritchie, sección A8.8 se indica:

las construcciones:

Blockno b;
extern Blockptr bp;
Complex z, *zp;

son declaraciones legítimas.

Y en la sección 2.4 alude a:

Una declaración especifica un tipo, y contiene una lista de una 0 mas variables de ese tipo, como en

int lower, upper, step;
char c, line [1000];

Metiendo en el mismo saco una «declaración con extern» y «una sin extern» y, para colmo, afirmando que las líneas anteriores son declaraciones (como suele pensar el 90% de los programadores de C) pero no es así, porque entonces algo como:

int contador;
int contador;

Obedecería la ODR pero es fácilmente comprobable que un compilador arroja un error de «redefinición». Por tanto, ambas son definiciones.

Las únicas formas de declarar sin definir son tres:

  1. Si se pretende declarar una función: proporcionando su tipo de retorno, nombre y tipos de sus parámetros pero sin proporcionar el cuerpo.
  2. Si se pretende declarar un tipo definido por el usuario: proporcionando el tipo de objeto (struct, class, enum…) y el nombre pero sin su definición propiamente dicha.
  3. Si se pretende declarar un tipo básico: utilizando la construcción la palabra reservada extern seguida del tipo y el identificador y nada más.

Para los tipos básicos, no indicar extern hará que se reserve memoria para la variable. En C, el contenido inicial de la variable era indeterminado («lo que hubiese antes allí», podríamos decir) mientras que en C++ el contenido se convierte en el 0 del tipo declarado.

extern int contador; // sólo declaración
int resultado; // declaración + definición (+ asignacion a 0 implícita)
int tamanio = 10; // declaración + definicion + asignación a 10 explícita
extern int indice = 1; // declaración + definición + asignación a 1 explícita

Como se puede apreciar, las líneas 3 y 4 hacen lo mismo: declaran, definen y asignan pero la construcción de la línea 4 debería evitarse. La palabra extern se escogio porque quería decir algo como «externamente definida«. Por ello, añadir una asignación que fuerza la definición de la variable «externamente definida» resulta un tanto contradictorio. No prohibir esta construcción es, a mi juicio, un error de la sintáxis de C++.

Y hasta aquí el tema de la ODR. Hay que añadir que el libro de Stroustrup es claro aquí aunque de pasada pues en él, en su apartado 4.9 se proporcionan diversos ejemplos de declaraciones y a continuación se hace notar que:

De las declaraciones anteriores, solamente

double sqrt(double);
extenr int numero_error;
struct usuario;

no son también definiciones.

En esta situación, la confusión viene de una «creencia popular de C».

Un comentario en “El lenguaje de programación C++: Declaraciones y One Definition Rule

  1. Esto es hilar muy fino, como me gusta a mí. La confusión viene principalmente, a mi juicio, por el hecho de que lo más común:

    int a;

    está declarando y definiendo. Y por otra parte, porque a base de utilizar el lenguaje, subconscientemente estás aprendiendo y aplicando esas diferencias.

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s