Está en la página 1de 26

IN TR O D U C C IÓ N A LA PR O G R A M A C IÓ N

O R IEN TA D A A O B JETO S
(IPO O )

AngelG arcía Baños


Escuela de Ingeniería de Sistem as y C om putación
U niversidad delValle
04 de Junio de 2001
IPOO clase 5

Paso de argumentos a funciones.


Referencias. Punteros.
PASO DE ARGUMENTOS A
FUNCIONES
Paso de argumentos a funciones
■ Al usar la interfase de un objeto, es habitual
proporcionar una serie de datos para que el objeto
los use.
■ También es habitual que el objeto retorne otros
datos.
■ Para especificar que datos se van a intercambiar, se
utilizan argumentos en las funciones miembro del
objeto. Bicicleta

void ir_hasta(intx,inty);

double kilom etros_recorridos();

AG B 4
Paso de argumentos a funciones
■ Diferenciemos entre:
– Lo que queremos hacer (semántica)
– Como hacerlo (mecanismos o sintaxis)

AG B 5
Paso de argumentos a funciones
■ Hay 3 tipos de semántica en los argumentos de las
funciones:
– Argumento de entrada: la función recibe el argumento
pero:
■ No puede alterarlo.
■ Recibe un duplicado, de modo que aunque lo altere, no se altera
el original.
– Argumento de salida: la función devuelve un argumento.
– Argumento de entrada-salida: la función recibe un
argumento, lo modifica y lo devuelve.

AG B 6
Paso de argumentos a funciones
■ En lenguaje C++ el paso de parámetros se efectúa
con la siguiente sintaxis:
– Los argumentos de entrada y de entrada-salida se
especifican justo después del nombre de la función,
encerrados en un único paréntesis, y separados por
comas. Eso es lo que se llama la lista de argumentos de
la función. Para cada argumento hay que especificar su
nombre (arbitrario) precedido de su correspondiente tipo.
// Retorna si se quemó:
bool Pollo::guisar(int kilos, int gramos_sal, double minutos);

– Puede haber 0 o 1 argumentos de salida. Se especifica


solo su tipo, justo antes del nombre de la función.
AG B 7
Paso de argumentos a funciones
■ El lenguaje C++ ofrece 3 mecanismos para
implementar el paso de argumentos a funciones:
– Por valor (se saca un duplicado del dato).
– Por referencia (alias o segundo nombre).
– Por puntero (dirección de memoria; o metáfora de la
piola).

AG B 8
Paso de argumentos por valor
■ Sintaxis del mecanismo “por valor”:
int main()
void Pollo::guisar(double tiempo)
{ horas 1.5
{
Pollo pollo; tiem po 1.5
while(tiempo-- > 0)
double horas = 1.5; 0.5
...
pollo.guisar(horas); -0.5
}
...
}
■ Se saca un duplicado del dato, es decir:
– Se crea una nueva variable tiempo, cuyo ámbito es la función
Pollo::guisar. (Mientras que el ámbito de horas es hasta el final de main)
– Se copia el valor de la variable inicial horas a la nueva variable tiempo.
– Son dos variables distintas e independientes. Sus ámbitos también son
distintos. Si se altera el valor de tiempo, no le afecta para nada a horas.
■ Por ello, este mecanismo de “paso por valor” se emplea para
datos de entrada. AG B 9
Paso de argumentos por referencia
■ Sintaxis del mecanismo “por referencia” (alias):
int main()
void Lavadora::encoger(int & valor)
{
{
Lavadora lavadora;
valor--;
int tallaRopa = 43;
}
lavadora.encoger(tallaRopa); Asíse declara
} la referencia valor
tallaR opa 43 valor
42
■ Ahora el dato es común a ambos ámbitos. El proceso es:
– Se crea un nuevo nombre (valor) para la variable tallaRopa.
– Los ámbitos de los nombre son distintos, pero se refieren a la misma
variable. Por ello, al decrementar valor, queda automáticamente
decrementado tallaRopa.
– valor debe llevar delante un &, en la declaración de la función.
■ Por ello, este mecanismo de “paso por referencia” se emplea
para datos de entrada-salida.AG B 10
Paso de argumentos por puntero
■ Sintaxis del mecanismo “por puntero” (dirección de
memoria o metáfora de la piola):
int main()
void Lavadora::encoger(int * valor)
{
{
Lavadora lavadora;
(*valor)--;
int tallaRopa = 43;
}
lavadora.encoger(& tallaRopa); Asíse declara
} elpuntero valor
Asíse usa elpuntero valor,
Asíse am arra elpuntero valor
para accederaldato que hay
aldato tallaR opa
alotro extrem o de la piola

tallaR opa 43 valor


42

AG B 11
Sintaxis de los punteros
■ Se puede acceder al dato tallaRopa desde los dos ámbitos:
main posee el dato, mientras que Lavadora::encoger
mantiene una piola amarrada a ese dato.
■ La sintaxis de los punteros es bastante complicada:
– Se declaran con *: int * valor;
– Se amarran al dato con &: lavadora.encoger(& tallaRopa);
– Se usan con *, para acceder al dato amarrado al otro extremo de la
piola: (*valor)--;
■ Es peligrosísimo usar un puntero sin haberlo amarrado
previamente:
int *valor; // Puntero no amarrado. Puede apuntar a cualquier sitio. PELIG R O
*valor = 5; // El 5 sobreescribe memoria que no nos pertenece. Core dump
■ Este mecanismo “por puntero” se emplea para datos de
entrada-salida.
AG B 12
Sintaxis de los punteros
■ Aparentemente los punteros hacen lo mismo que las
referencias. Sin embargo, son mas potentes, puesto que
también permiten cambiar con mucha facilidad el extremo a
donde está amarrada la piola:
double datos[100];
double *puntero = & datos[9]; // Se amarra (apunta) al dato número 9
*puntero = 0; // Pone a 0 el dato número 9
puntero++; // Pasa a apuntar al siguiente dato
*puntero = 0; // Pone a 0 el dato número 10
puntero -= 3; // Retrocede 3 posiciones
*puntero = 4; // Pone a 4 el dato número 7
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 99
datos 4 0 0

puntero
AG B 13
Otros mecanismos
■ Acabamos de ver los tres mecanismos básicos de
C++, para el paso de argumentos:
– “por valor” que sirve para lograr semántica “de entrada”.
– “por referencia” para semántica “de entrada-salida”.
– “por puntero” para semántica “de entrada-salida”.
■ Pero existen otras combinaciones, que veremos a
continuación. La mayoría se basan en el uso del
modificador const.
■ Además también veremos las diversas maneras de
retornar un valor (argumento de salida).

AG B 14
Paso de argumentos de entrada
■ Ejemplo argumentos de entrada: IPO O _5_1

– Por valor.
– Por referencia a una constante.
– Por puntero a una constante.

void Loro::te_tengo_un_chism e(string chism e);

void Loro::observar_com ida(constH elado & helado);

void Loro::observar_com ida(constH elado *helado);

AG B 15
Paso de argumentos de entrada-salida
■ Ejemplo argumentos de entrada-salida: IPO O _5_1

– Por referencia.
– Por puntero.

void Loro::com er(H elado & helado);

void Loro::com er(H elado *helado);

AG B 16
Paso de argumentos de salida
IPO O _5_1

■ Ejemplo argumento de salida (valor de retorno):


– Por valor.
– Por referencia.
– Por referencia a una constante.
– Por puntero.
– Por puntero a una constante.
boolLoro::tienes_ham bre();

int & Loro::cual_es_tu_edad();

conststring & Loro::com o_te_llam as();

double * Loro::cuanto_pesas();

conststring * Loro::chao();

AG B 17
El argumento de salida
■ Si no hay valor de retorno, se especifica con el tipo
void.
■ El hecho de que solo se admita como máximo un
valor de retorno es para facilitar el encadenamiento
de funciones:
Cada función, al evaluarse, devuelve un argumento de
salida. Es decir, la función es equivalente a ese
argumento de salida. Ejemplo:
double y =0.5;
double x = sin(3 * abs(y - 0.7));

AG B 18
El argumento de salida
– La precedencia de los paréntesis fuerza a evaluar primero
la resta y-0.7. Su resultado de salida es -0.2. Por tanto,
es como si estuviera escrito:
double x = sin(3 * abs(-0.2));
– A continuación se evalúa la función abs(-0.2). El prototipo
de esta función es:
double abs(double);
– Ello significa que recibe un argumento de entrada (por
valor) de tipo double y retorna un argumento de salida
(por valor) de tipo double.
– El resultado de evaluar abs(-0.2) es 0.2, por lo que es
como si la expresión se hubiera reducido a:
double x = sin(3 * 0.2);
AG B 19
El argumento de salida
– La multiplicación de 3*0.2 da 0.6, por lo que la expresión
se reduce a:
double x = sin(0.6);
– Por último se ejecuta la función sin() cuyo prototipo es:
double sin(double);
– lo cual significa que recibe un argumento de entrada (por
valor) de tipo double y retorna un argumento de salida
(por valor) de tipo double.
– Finalmente la expresión queda reducida a la asignación:
double x = 0.0104717;

AG B 20
El argumento de salida
■ Problemas que hay que evitar: PELIG R O
– En el caso de argumentos de salida (y de entrada-salida) por puntero
o referencia, vigilar no se vaya a destruir el dato al terminar la función.
– Ejemplo incorrecto (el dato no existe, cuando la función termina):

#include “helado.h” #include “helado.h”


#include “fabricahelados.h”
Helado *FabricaHelados::crear()
{ int main()
Helado unHelado; {
return &unHelado; // Aquí se FabricaHelados fabricaHelados;
// destruye unHelado porque sale Helado *miHelado = fabricaHelados.crear();
// fuera de ámbito miHelado->comer(); // Core Dump
}
}

AG B 21
El argumento de salida
■ Una posible solución: manejarlo como puntero: PELIG R O
#include “helado.h” #include “helado.h”
#include “fabricahelados.h”
Helado * FabricaHelados::crear()
{ int main()
Helado *unHelado = new Helado; {
return unHelado; // Aquí se FabricaHelados fabricaHelados;
// destruye el puntero unHelado, Helado*miHelado=fabricaHelados.crear();
// pero no el objeto al que (*miHelado).comer(); // OK
// apunta }
}

■ Otra posible solución: manejarlo como dato de entrada-salida


(el dato se crea por fuera de la función, por lo que no se
destruye cuando la función termina).
AG B 22
El argumento de salida
■ Otra posible solución: retornarlo por valor (aunque es PELIG R O
ineficiente): #include “helado.h”
#include “helado.h” #include “fabricahelados.h”

Helado FabricaHelados::crear() int main()


{ {
Helado unHelado; FabricaHelados fabricaHelados;
return unHelado; // Aquí se Helado miHelado=fabricaHelados.crear();
// destruye unHelado, pero antes le miHelado.comer(); // OK
// saca una copia (paso por valor) }
// que retorna.
}

■ Otra posible solución: manejarlo como dato de entrada-salida


(el dato se crea por fuera de la función, por lo que no se
destruye cuando la función termina).
AG B 23
El argumento de salida

■ Mas problemas que hay que evitar: PR O H IB ID O


EN O O
– No es elegante devolver un puntero o referencia a un dato
interno de un objeto, ya que se rompe su encapsulamiento
(otros objetos pueden alterar su estado interno). Ejemplo
incorrecto: (ver también IPO O _5_1 al final).
class Nevera
{
private:
Helado miHelado;
public:
Helado *prestarHelado(); // Retornar puntero a dato privado está prohibido en POO
}; // Retornar una referencia a un dato privado se permite si se documenta bien
– Si que es válido devolver un puntero constante o una referencia
constante a un dato interno del objeto. Ejemplo correcto, ver:
IPO O _5_2
AG B 24
El argumento de salida

■ En la transparencia anterior “interno” está PR EN


O H IB ID O
OO
subrayado. Lo que quiero decir es que sí es
perfectamente válido retornar punteros o
referencias a datos que no pertenezcan al objeto ni
sean locales a la función.
■ Por ejemplo, es válido retornar un puntero o
referencia a un dato creado dinámicamente.

AG B 25
Aclaraciones acerca de punteros y
referencias
■ No es lo mismo:
– Puntero a una constante: la variable a la cual apunta no se puede cambiar.
– Puntero constante: el puntero siempre apunta a la misma variable.
■ Las referencias siempre son constantes (apuntan al mismo sitio, es decir,
son el alias de la misma variable). Y pueden apuntar a variables o a
constantes.
const int array[2] = { 5, 8 }; const int array[2] = { 5, 8 };
const int *p = &array[0]; // p apunta a una const int &r = array[0]; // r es alias
// constante // de una constante. Y siempre lo será.
p++; // OK, p apunta a array[1] r++; // Ilegal, por ser alias de const
*p = 0; // Ilegal, ya que array[1] es una r = 0; // Ilegal, por ser alias de const
// constante
int otro[2] = { 5, 8 };
int valor = 34; int &r = otro[1]; // r es alias de una
int * const q = &valor; // q es un puntero // variable
// constante r++; // OK, otro[1] = 9
(*q)++; // valor pasa a valer 35 r = 0; // OK, otro[1] = 0
q++; // Ilegal AG B 26

También podría gustarte