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 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 ✐ ✐ ✐ ✐

✐<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 />

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

Saved successfully!

Ooh no, something went wrong!