Pensar en C++ (Volumen 1) - Grupo ARCO
Pensar en C++ (Volumen 1) - Grupo ARCO Pensar en C++ (Volumen 1) - Grupo ARCO
✐ ✐ ✐ “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 ✐ ✐ ✐ ✐
- Page 147 and 148: ✐ ✐ ✐ “Volumen1” — 2012
- Page 149 and 150: ✐ ✐ ✐ “Volumen1” — 2012
- Page 151 and 152: ✐ ✐ ✐ “Volumen1” — 2012
- Page 153 and 154: ✐ ✐ ✐ “Volumen1” — 2012
- Page 155 and 156: ✐ ✐ ✐ “Volumen1” — 2012
- Page 157 and 158: ✐ ✐ ✐ “Volumen1” — 2012
- Page 159 and 160: ✐ ✐ ✐ “Volumen1” — 2012
- Page 161 and 162: ✐ ✐ ✐ “Volumen1” — 2012
- Page 163 and 164: ✐ ✐ ✐ “Volumen1” — 2012
- Page 165 and 166: ✐ ✐ ✐ “Volumen1” — 2012
- Page 167 and 168: ✐ ✐ ✐ “Volumen1” — 2012
- Page 169 and 170: ✐ ✐ ✐ “Volumen1” — 2012
- Page 171 and 172: ✐ ✐ ✐ “Volumen1” — 2012
- Page 173 and 174: ✐ ✐ ✐ “Volumen1” — 2012
- Page 175 and 176: ✐ ✐ ✐ “Volumen1” — 2012
- Page 177 and 178: ✐ ✐ ✐ “Volumen1” — 2012
- Page 179 and 180: ✐ ✐ ✐ “Volumen1” — 2012
- Page 181 and 182: ✐ ✐ ✐ “Volumen1” — 2012
- Page 183 and 184: ✐ ✐ ✐ “Volumen1” — 2012
- Page 185 and 186: ✐ ✐ ✐ “Volumen1” — 2012
- Page 187 and 188: ✐ ✐ ✐ “Volumen1” — 2012
- Page 189 and 190: ✐ ✐ ✐ “Volumen1” — 2012
- Page 191 and 192: ✐ ✐ ✐ “Volumen1” — 2012
- Page 193 and 194: ✐ ✐ ✐ “Volumen1” — 2012
- Page 195 and 196: ✐ ✐ ✐ “Volumen1” — 2012
- Page 197: ✐ ✐ ✐ “Volumen1” — 2012
- Page 201 and 202: ✐ ✐ ✐ “Volumen1” — 2012
- Page 203 and 204: ✐ ✐ ✐ “Volumen1” — 2012
- Page 205 and 206: ✐ ✐ ✐ “Volumen1” — 2012
- Page 207 and 208: ✐ ✐ ✐ “Volumen1” — 2012
- Page 209 and 210: ✐ ✐ ✐ “Volumen1” — 2012
- Page 211 and 212: ✐ ✐ ✐ “Volumen1” — 2012
- Page 213 and 214: ✐ ✐ ✐ “Volumen1” — 2012
- Page 215 and 216: ✐ ✐ ✐ “Volumen1” — 2012
- Page 217 and 218: ✐ ✐ ✐ “Volumen1” — 2012
- Page 219 and 220: ✐ ✐ ✐ “Volumen1” — 2012
- Page 221 and 222: ✐ ✐ ✐ “Volumen1” — 2012
- Page 223 and 224: ✐ ✐ ✐ “Volumen1” — 2012
- Page 225 and 226: ✐ ✐ ✐ “Volumen1” — 2012
- Page 227 and 228: ✐ ✐ ✐ “Volumen1” — 2012
- Page 229 and 230: ✐ ✐ ✐ “Volumen1” — 2012
- Page 231 and 232: ✐ ✐ ✐ “Volumen1” — 2012
- Page 233 and 234: ✐ ✐ ✐ “Volumen1” — 2012
- Page 235 and 236: ✐ ✐ ✐ “Volumen1” — 2012
- Page 237 and 238: ✐ ✐ ✐ “Volumen1” — 2012
- Page 239 and 240: ✐ ✐ ✐ “Volumen1” — 2012
- Page 241 and 242: ✐ ✐ ✐ “Volumen1” — 2012
- Page 243 and 244: ✐ ✐ ✐ “Volumen1” — 2012
- Page 245 and 246: ✐ ✐ ✐ “Volumen1” — 2012
- Page 247 and 248: ✐ ✐ ✐ “Volumen1” — 2012
✐<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 />
✐