Pensar en C++ (Volumen 1) - Grupo ARCO

Pensar en C++ (Volumen 1) - Grupo ARCO Pensar en C++ (Volumen 1) - Grupo ARCO

arco.esi.uclm.es
from arco.esi.uclm.es More from this publisher
13.01.2015 Views

✐ ✐ ✐ “Volumen1” — 2012/1/12 — 13:52 — page 160 — #198 ✐ Capítulo 4. Abstracción de Datos traducción en un mismo proyecto, y si el almacenamiento para un identificador se pide en más de un sitio, el enlazador indicará un error de definición múltiple (ésta es la regla de definición única de C++: Se puede declarar tantas veces como se quiera, pero sólo puede haber una definición real para cada cosa). Esta norma no es completamente estricta. Si se define una variable que es «file static» (que tiene visibilidad sólo en un fichero) dentro de un fichero de cabecera, habrá múltiples instancias de ese dato a lo largo del proyecto, pero no causará un colisión en el enlazador 6 . Básicamente, debe evitar cualquier cosa en los ficheros de cabecera que pueda causar una ambigüedad en tiempo de enlazado. 4.7.2. El problema de la declaración múltiple La segunda cuestión respecto a los ficheros de cabecera es ésta: cuando se pone una declaración de struct en un fichero de cabecera, es posible que el fichero sea incluido más de una vez en un programa complicado. Los iostreams son un buen ejemplo. Cada vez que una struct hace E/S debe incluir uno de los ficheros de cabecera iostream. Si el fichero cpp sobre el que se está trabajando utiliza más de un tipo de struct (típicamente incluyendo un fichero de cabecera para cada una), se está corriendo el riesgo de incluir el fichero más de una vez y redeclarar los iostreams. El compilador considera que la redeclaración de una estructura (eso es aplicable tando a las struct como a las class) es un error, dado que de otro modo, debería permitir el uso del mismo nombre para tipos diferentes. Para evitar este error cuando se incluyen múltiples ficheros de cabecera, es necesario dar algo de inteligencia a los ficheros de cabecera usando el preprocesador (los ficheros de cabecera estándares como también tienen esta «inteligencia»). Tanto C como C++ permiten redeclarar una función, siempre que las dos declaraciones coincidan, pero ni en ese caso se permite la redeclaración de una estructura. En C++ esta regla es especialmente importante porque si el compilador permitiera la redeclaración de una estructura y las dos declaraciones difirieran, ¿cuál debería usar El problema de la redeclaración se agrava un poco en C++ porque cada tipo de dato (estructura con funciones) generalmente tiene su propio fichero de cabecera, y hay que incluir un fichero de cabecera en otro si se quiere crear otro tipo de dato que use al primero. Es probable que en algún fichero cpp de su proyecto, que se incluyan varios ficheros que incluyan al mismo fichero de cabecera. Durante una compilación simple, el compilador puede ver el mismo fichero de cabecera varias veces. A menos que se haga algo al respecto, el compilador verá la redeclaración de la estructura e informará un error en tiempo de compilación. Para resolver el problema, necesitará saber un poco más acerca del preprocesador. 4.7.3. Las directivas del preprocesador #define, #ifndef y #endif La directiva de preprocesador #define se puede usar para crear banderas en tiempo de compilación. Tiene dos opciones: puede simplemente indicar al preprocesador que la bandera está definida, sin especificar un valor: 6 Sin embargo, en C++ estándar «file static» es una característica obsoleta. 160 ✐ ✐ ✐ ✐

✐ ✐ ✐ “Volumen1” — 2012/1/12 — 13:52 — page 161 — #199 ✐ 4.7. Conveciones para los ficheros de cabecera #define FLAG o puede darle un valor (que es la manera habitual en C para definir una constante): #define PI 3.14159 En cualquier caso, ahora el preprocesador puede comprobar si la etiqueta ha sido definida: #ifdef FLAG Esto producirá un resultado verdadero, y el código que sigue al #ifdef se incluirá en el paquete que se envía al compilador. Esta inclusión acaba cuando el preprocesador encuentra la sentencia: #endif o #endif // FLAG Cualquier cosa después de #endif en la misma línea que no sea un comentario es ilegal, incluso aunque algunos compiladores lo acepten. Los pares #ifdef/#endif se pueden anidar. El complementario de #define es #undef (abreviación de «un-define» que hará que una sentencia #ifdef que use la misma variable produzca un resultado falso. #undef también causará que el preprocesador deje de usar una macro. El complementario de #ifdef es #ifndef, que producirá verdadero si la etiqueta no ha sido definida (éste es el que usaremos en los ficheros de cabecera). Hay otras características útiles en el preprocesador de C. Consulte la documentación de su preprocesador para ver todas ellas. 4.7.4. Un estándar para los ficheros de cabecera En cada fichero de cabecera que contiene una estructura, primero debería comprobar si ese fichero ya ha sido includo en este fichero cpp particular. Hágalo comprobando una bandera del preprocesador. Si la bandera no está definida, el fichero no se ha incluido aún, y se debería definir la bandera (de modo que la estructura no se pueda redeclarar) y declarar la estructura. Si la bandera estaba definida entonces el tipo ya ha sido declarado de modo que debería ignorar el código que la declara. Así es como debería ser un fichero de cabecera: #ifndef HEADER_FLAG #define HEADER_FLAG // Escriba la ódeclaracin íaqu... #endif // HEADER_FLAG 161 ✐ ✐ ✐ ✐

✐<br />

✐<br />

✐<br />

“Volum<strong>en</strong>1” — 2012/1/12 — 13:52 — page 160 — #198<br />

✐<br />

Capítulo 4. Abstracción de Datos<br />

traducción <strong>en</strong> un mismo proyecto, y si el almac<strong>en</strong>ami<strong>en</strong>to para un id<strong>en</strong>tificador se<br />

pide <strong>en</strong> más de un sitio, el <strong>en</strong>lazador indicará un error de definición múltiple (ésta<br />

es la regla de definición única de <strong>C++</strong>: Se puede declarar tantas veces como se quiera,<br />

pero sólo puede haber una definición real para cada cosa).<br />

Esta norma no es completam<strong>en</strong>te estricta. Si se define una variable que es «file<br />

static» (que ti<strong>en</strong>e visibilidad sólo <strong>en</strong> un fichero) d<strong>en</strong>tro de un fichero de cabecera,<br />

habrá múltiples instancias de ese dato a lo largo del proyecto, pero no causará un<br />

colisión <strong>en</strong> el <strong>en</strong>lazador 6 . Básicam<strong>en</strong>te, debe evitar cualquier cosa <strong>en</strong> los ficheros de<br />

cabecera que pueda causar una ambigüedad <strong>en</strong> tiempo de <strong>en</strong>lazado.<br />

4.7.2. El problema de la declaración múltiple<br />

La segunda cuestión respecto a los ficheros de cabecera es ésta: cuando se pone<br />

una declaración de struct <strong>en</strong> un fichero de cabecera, es posible que el fichero sea<br />

incluido más de una vez <strong>en</strong> un programa complicado. Los iostreams son un bu<strong>en</strong><br />

ejemplo. Cada vez que una struct hace E/S debe incluir uno de los ficheros de<br />

cabecera iostream. Si el fichero cpp sobre el que se está trabajando utiliza más de<br />

un tipo de struct (típicam<strong>en</strong>te incluy<strong>en</strong>do un fichero de cabecera para cada una),<br />

se está corri<strong>en</strong>do el riesgo de incluir el fichero más de una vez y redeclarar<br />

los iostreams.<br />

El compilador considera que la redeclaración de una estructura (eso es aplicable<br />

tando a las struct como a las class) es un error, dado que de otro modo, debería<br />

permitir el uso del mismo nombre para tipos difer<strong>en</strong>tes. Para evitar este error cuando<br />

se incluy<strong>en</strong> múltiples ficheros de cabecera, es necesario dar algo de intelig<strong>en</strong>cia a los<br />

ficheros de cabecera usando el preprocesador (los ficheros de cabecera estándares<br />

como también ti<strong>en</strong><strong>en</strong> esta «intelig<strong>en</strong>cia»).<br />

Tanto C como <strong>C++</strong> permit<strong>en</strong> redeclarar una función, siempre que las dos declaraciones<br />

coincidan, pero ni <strong>en</strong> ese caso se permite la redeclaración de una estructura.<br />

En <strong>C++</strong> esta regla es especialm<strong>en</strong>te importante porque si el compilador permitiera<br />

la redeclaración de una estructura y las dos declaraciones difirieran, ¿cuál debería<br />

usar<br />

El problema de la redeclaración se agrava un poco <strong>en</strong> <strong>C++</strong> porque cada tipo de<br />

dato (estructura con funciones) g<strong>en</strong>eralm<strong>en</strong>te ti<strong>en</strong>e su propio fichero de cabecera, y<br />

hay que incluir un fichero de cabecera <strong>en</strong> otro si se quiere crear otro tipo de dato que<br />

use al primero. Es probable que <strong>en</strong> algún fichero cpp de su proyecto, que se incluyan<br />

varios ficheros que incluyan al mismo fichero de cabecera. Durante una compilación<br />

simple, el compilador puede ver el mismo fichero de cabecera varias veces. A m<strong>en</strong>os<br />

que se haga algo al respecto, el compilador verá la redeclaración de la estructura e<br />

informará un error <strong>en</strong> tiempo de compilación. Para resolver el problema, necesitará<br />

saber un poco más acerca del preprocesador.<br />

4.7.3. Las directivas del preprocesador #define, #ifndef y<br />

#<strong>en</strong>dif<br />

La directiva de preprocesador #define se puede usar para crear banderas <strong>en</strong><br />

tiempo de compilación. Ti<strong>en</strong>e dos opciones: puede simplem<strong>en</strong>te indicar al preprocesador<br />

que la bandera está definida, sin especificar un valor:<br />

6 Sin embargo, <strong>en</strong> <strong>C++</strong> estándar «file static» es una característica obsoleta.<br />

160<br />

✐<br />

✐<br />

✐<br />

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!