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 206 — #244 ✐ Capítulo 7. Sobrecarga de funciones y argumentos por defecto res, es también de utilidad general para cualquier función, incluso aquellas que no son métodos. Además, la sobrecarga de funciones significa que si tiene dos librerías que contienen funciones con el mismo nombre, no entrarán en conflicto siempre y cuando las listas de argumentos sean diferentes. A lo largo del capítulo se mostrarán todos los detalles. El tema de este capítulo es la elección adecuada de los nombres de la funciones. La sobrecarga de funciones permite utilizar el mismo nombre para funciones diferentes, pero hay otra forma más adecuada de llamar a una función. ¿Qué ocurriría si le gustara llamar a la misma función de formas diferentes Cuando las funciones tienen una larga lista de argumentos, puede resultar tediosa la escritura (y confusa la lectura) de las llamadas a la función cuando la mayoría de los argumentos son lo mismos para todas las llamadas. Una característica de C++ comúnmente utilizada se llama argumento por defecto. Un argumento por defecto es aquel que el compilador inserta en caso de que no se especifique cuando se llama a la función. Así, las llamadas f("hello"), f("hi", 1) y f("howdy", 2, ’c’) pueden ser llamadas a la misma función. También podrían ser llamadas a tres funciones sobrecargadas, pero cuando las listas de argumentos son tan similares, querrá que tengan un comportamiento similar, que le lleva a tener una única función. La sobrecarga de funciones y los argumentos por defecto no son muy complicados. En el momento en que termine este capítulo, sabrá cuándo utilizarlos y entenderá los mecanismos internos que el compilador utiliza en tiempo de compilación y enlace. 7.1. Más decoración de nombres En el Capítulo 4 se presentó el concepto de decoración de nombres. En el código: void f(); class X { void f(); }; La función f() dentro del ámbito de la clase X no entra en conflicto con la versión global de f(). El compilador resuelve los ámbitos generando diferentes nombres internos tanto para la versión global de f() como para X::f(). En el Capítulo 4 se sugirió que los nombres son simplemente el nombre de la clase junto con el nombre de la función. Un ejemplo podría ser que el compilador utilizara como nombres _f y _X_f. Sin embargo ahora se ve que la decoración del nombre de la función involucra algo más que el nombre de la clase. He aquí el porqué. Suponga que quiere sobrecargar dos funciones void print(char); void print(float); No importa si son globales o están dentro de una clase. El compilador no puede generar identificadores internos únicos si sólo utiliza el ámbito de las funciones. Terminaría con _print en ambos casos. La idea de una función sobrecargada es que se utilice el mismo nombre de función, pero diferente lista de argumentos. Así pues, para que la sobrecarga funcione el compilador ha de decorar el nombre de la función con los nombres de los tipos de los argumentos. Las funciones planteadas más arriba, definidas como globales, producen nombres internos que podrían parecerse a algo así como _print_char y _print_float. Nótese que como no hay 206 ✐ ✐ ✐ ✐
✐ ✐ ✐ “Volumen1” — 2012/1/12 — 13:52 — page 207 — #245 ✐ 7.1. Más decoración de nombres ningún estándar de decoración, podrá obtener resultados diferentes de un compilador a otro. (Puede ver lo que saldría diciéndole al compilador que genere código fuente en ensamblador). Esto, por supuesto, causa problemas si desea comprar unas librerías compiladas por un compilador y enlazador particulares, aunque si la decoración de nombres fuera estándar, habría otros obstáculos debido a las diferencias de generación de código máquina entre compiladores. Esto es todo lo que hay para la sobrecarga de funciones: puede utilizar el mismo nombre de función siempre y cuando la lista de argumentos sea diferente. El compilador utiliza el nombre, el ámbito y la lista de argumentos para generar un nombre interno que el enlazador pueda utilizar. 7.1.1. Sobrecarga en el valor de retorno Es muy común la pregunta «¿Por qué solamente el ámbito y la lista de argumentos ¿Por qué no también el valor de retorno». A primera vista parece que tendría sentido utilizar también el valor de retorno para la decoración del nombre interno. De esta manera, también podría sobrecargar con los valores de retorno: void f(); int f(); Esto funciona bien cuando el compilador puede determinar sin ambigüedades a qué tipo de valor de retorno se refiere, como en int x = f();. No obstante, en C se puede llamar a una función y hacer caso omiso del valor de retorno (esto es, puede querer llamar a la función debido a sus efectos laterales). ¿Cómo puede el compilador distinguir a qué función se refiere en este caso Peor es la dificultad que tiene el lector del código fuente para dilucidar a qué función se refiere. La sobrecarga mediante el valor de retorno solamente es demasiado sutil, por lo que C++ no lo permite. 7.1.2. Enlace con FIXME:tipos seguros Existe un beneficio añadido a la decoración de nombres. En C hay un problema particularmente fastidioso cuando un programador cliente declara mal una función o, aún peor, se llama a una función sin haber sido previamente declarada, y el compilador infiere la declaración de la función mediante la forma en que se llama. Algunas veces la declaración de la función es correcta, pero cuando no lo es, suele resultar en un fallo difícil de encontrar. A causa de que en C++ se deben declarar todas las funciones antes de llamarlas, las probabilidades de que ocurra lo anteriormente expuesto se reducen drásticamente. El compilador de C++ rechaza declarar una función automáticamente, así que es probable que tenga que incluir la cabecera apropiada. Sin embargo, si por alguna razón se las apaña para declarar mal una función, o declararla a mano o incluir una cabecera incorrecta (quizá una que sea antigua), la decoración de nombres proporciona una seguridad que a menudo se denomina como enlace con tipos seguros. Considere el siguiente escenario. En un fichero está la definición de una función: //: C07:Def.cpp {O} // Function definition void f(int) {} ///:~ 207 ✐ ✐ ✐ ✐
- Page 193 and 194: ✐ ✐ ✐ “Volumen1” — 2012
- Page 195 and 196: ✐ ✐ ✐ “Volumen1” — 2012
- Page 197 and 198: ✐ ✐ ✐ “Volumen1” — 2012
- Page 199 and 200: ✐ ✐ ✐ “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: ✐ ✐ ✐ “Volumen1” — 2012
- Page 247 and 248: ✐ ✐ ✐ “Volumen1” — 2012
- Page 249 and 250: ✐ ✐ ✐ “Volumen1” — 2012
- Page 251 and 252: ✐ ✐ ✐ “Volumen1” — 2012
- Page 253 and 254: ✐ ✐ ✐ “Volumen1” — 2012
- Page 255 and 256: ✐ ✐ ✐ “Volumen1” — 2012
- Page 257 and 258: ✐ ✐ ✐ “Volumen1” — 2012
- Page 259 and 260: ✐ ✐ ✐ “Volumen1” — 2012
- Page 261 and 262: ✐ ✐ ✐ “Volumen1” — 2012
- Page 263 and 264: ✐ ✐ ✐ “Volumen1” — 2012
- Page 265 and 266: ✐ ✐ ✐ “Volumen1” — 2012
- Page 267 and 268: ✐ ✐ ✐ “Volumen1” — 2012
- Page 269 and 270: ✐ ✐ ✐ “Volumen1” — 2012
- Page 271 and 272: ✐ ✐ ✐ “Volumen1” — 2012
- Page 273 and 274: ✐ ✐ ✐ “Volumen1” — 2012
- Page 275 and 276: ✐ ✐ ✐ “Volumen1” — 2012
- Page 277 and 278: ✐ ✐ ✐ “Volumen1” — 2012
- Page 279 and 280: ✐ ✐ ✐ “Volumen1” — 2012
- Page 281 and 282: ✐ ✐ ✐ “Volumen1” — 2012
- Page 283 and 284: ✐ ✐ ✐ “Volumen1” — 2012
- Page 285 and 286: ✐ ✐ ✐ “Volumen1” — 2012
- Page 287 and 288: ✐ ✐ ✐ “Volumen1” — 2012
- Page 289 and 290: ✐ ✐ ✐ “Volumen1” — 2012
- Page 291 and 292: ✐ ✐ ✐ “Volumen1” — 2012
- Page 293 and 294: ✐ ✐ ✐ “Volumen1” — 2012
✐<br />
✐<br />
✐<br />
“Volum<strong>en</strong>1” — 2012/1/12 — 13:52 — page 206 — #244<br />
✐<br />
Capítulo 7. Sobrecarga de funciones y argum<strong>en</strong>tos por defecto<br />
res, es también de utilidad g<strong>en</strong>eral para cualquier función, incluso aquellas que no<br />
son métodos. Además, la sobrecarga de funciones significa que si ti<strong>en</strong>e dos librerías<br />
que conti<strong>en</strong><strong>en</strong> funciones con el mismo nombre, no <strong>en</strong>trarán <strong>en</strong> conflicto siempre y<br />
cuando las listas de argum<strong>en</strong>tos sean difer<strong>en</strong>tes. A lo largo del capítulo se mostrarán<br />
todos los detalles.<br />
El tema de este capítulo es la elección adecuada de los nombres de la funciones.<br />
La sobrecarga de funciones permite utilizar el mismo nombre para funciones difer<strong>en</strong>tes,<br />
pero hay otra forma más adecuada de llamar a una función. ¿Qué ocurriría<br />
si le gustara llamar a la misma función de formas difer<strong>en</strong>tes Cuando las funciones<br />
ti<strong>en</strong><strong>en</strong> una larga lista de argum<strong>en</strong>tos, puede resultar tediosa la escritura (y confusa<br />
la lectura) de las llamadas a la función cuando la mayoría de los argum<strong>en</strong>tos son lo<br />
mismos para todas las llamadas. Una característica de <strong>C++</strong> comúnm<strong>en</strong>te utilizada<br />
se llama argum<strong>en</strong>to por defecto. Un argum<strong>en</strong>to por defecto es aquel que el compilador<br />
inserta <strong>en</strong> caso de que no se especifique cuando se llama a la función. Así, las llamadas<br />
f("hello"), f("hi", 1) y f("howdy", 2, ’c’) pued<strong>en</strong> ser llamadas<br />
a la misma función. También podrían ser llamadas a tres funciones sobrecargadas,<br />
pero cuando las listas de argum<strong>en</strong>tos son tan similares, querrá que t<strong>en</strong>gan un comportami<strong>en</strong>to<br />
similar, que le lleva a t<strong>en</strong>er una única función.<br />
La sobrecarga de funciones y los argum<strong>en</strong>tos por defecto no son muy complicados.<br />
En el mom<strong>en</strong>to <strong>en</strong> que termine este capítulo, sabrá cuándo utilizarlos y <strong>en</strong>t<strong>en</strong>derá<br />
los mecanismos internos que el compilador utiliza <strong>en</strong> tiempo de compilación y<br />
<strong>en</strong>lace.<br />
7.1. Más decoración de nombres<br />
En el Capítulo 4 se pres<strong>en</strong>tó el concepto de decoración de nombres. En el código:<br />
void f();<br />
class X { void f(); };<br />
La función f() d<strong>en</strong>tro del ámbito de la clase X no <strong>en</strong>tra <strong>en</strong> conflicto con la versión<br />
global de f(). El compilador resuelve los ámbitos g<strong>en</strong>erando difer<strong>en</strong>tes nombres<br />
internos tanto para la versión global de f() como para X::f(). En el Capítulo 4 se<br />
sugirió que los nombres son simplem<strong>en</strong>te el nombre de la clase junto con el nombre<br />
de la función. Un ejemplo podría ser que el compilador utilizara como nombres _f y<br />
_X_f. Sin embargo ahora se ve que la decoración del nombre de la función involucra<br />
algo más que el nombre de la clase.<br />
He aquí el porqué. Suponga que quiere sobrecargar dos funciones<br />
void print(char);<br />
void print(float);<br />
No importa si son globales o están d<strong>en</strong>tro de una clase. El compilador no puede<br />
g<strong>en</strong>erar id<strong>en</strong>tificadores internos únicos si sólo utiliza el ámbito de las funciones.<br />
Terminaría con _print <strong>en</strong> ambos casos. La idea de una función sobrecargada es<br />
que se utilice el mismo nombre de función, pero difer<strong>en</strong>te lista de argum<strong>en</strong>tos. Así<br />
pues, para que la sobrecarga funcione el compilador ha de decorar el nombre de la<br />
función con los nombres de los tipos de los argum<strong>en</strong>tos. Las funciones planteadas<br />
más arriba, definidas como globales, produc<strong>en</strong> nombres internos que podrían parecerse<br />
a algo así como _print_char y _print_float. Nótese que como no hay<br />
206<br />
✐<br />
✐<br />
✐<br />
✐