[C con Clase] Dudas con operador

Salvador Pozo salvador en conclase.net
Vie Ago 24 11:56:32 CEST 2012


El pasado 2012-08-23 17:01:22, david g, escribió:
 
dg> Buenas tardes, soy novato y sin idea de programación hasta ahora mas o menos voy entendiendo la estructura pero no entiendo concretamente el ejercicio 6.2 del epigrafe 6 del curso c++, concretamente no se como funciona este operador bool:

Hola:

Empecemos por aclarar algunas cosas:

bool no es un operador, sino un tipo fundamental C++. Es decir, se pueden crear objetos o variables de tipo bool, se pueden usar valores bool como parámetros de funciones y como valores de retorno.

Es cierto que, como todos los tipos fundamentales, C++ crea un operador bool para realizar cambios de tipo, pero eso es sólo una consecuencia de ser un tipo fundamental. Por ejemplo:

----8<------
int x = 10;
bool bx;

bx = bool(x);
----8<------

En este caso, el primer bool se usa para declarar una variable, bx. El segundo se usa para convertir un valor (el de la variable x) a bool.

También en este segundo caso se trata de una operación redundante, es decir, se podría omitir el operador:

----8<------
bx = x;
----8<------

El compilador es capaz de encontrar el operador de cambio de tipo por su cuenta, e invocarlo de forma implícita, sin que sea necesario que nosotros lo escribamos.

Ahora creo que puedo explicar tus dudas sobre el ejemplo que mencionas, sobre todo porque lo escribí yo, y sé exactamente qué pretendía al hacerlo :).

Una de las cosas que pretendía es que alguien hiciera esta pregunta. XD

dg> - en la linea " if (multiplodetres(i))cout << " es multiplo de 3 ";" no se que  función tiene (i), 

Ten en cuenta que esta sentencia está dentro de un bucle, en el que la variable i va tomando valores entre 1 y 20. El enunciado del ejercicio indica que se debe mostrar para cada número si es o no divisible por tres.

"MultiploDeTres" es una función. El nombre ha sido elegido para que sea evidente qué hace la función. Es una norma que conviene seguir siempre que se pueda, ya que hace más legible los programas.

Traduzcamos a español lo que hace la línea que indica:

Si (i) es MultiploDeTres mostrar la cadena "es multiplo de 3", en caso contrario mostrar la cadena "no es multiplo de tres".

Esto lo hace con los valores que i va tomando a medida que se ejecuta el bucle while:

----8<-----
   int i = 1; // variable para bucle

   while(i <= 20) // bucle hasta i igual a 20
   {
...
----8<-----

Esta es la idea, pero veamos por qué funciona correctamente esta sentencia.

José Roberto ha respondido también a este mensaje, y ha añadido algunas cosas al programa, esta sentencia, por ejemplo, ha quedado así:

----8<------
if (MultiploDeTres(h)==true)
    cout<<"Es un multiplo de 3"<<endl;
else
    cout<<"No es multiplo de 3"<<endl;
----8<------

Sin embargo, comparar el valor de retorno de "MultiploDeTres" con true es redundante. La sentencia if evalúa lo que hay entre paréntesis como un valor bool, si el resultado es true ejecuta la sentencia a continuación, y si es false, ejecuta la sentencia a continuación del else, si está presente.

Por lo tanto (MultiploDeTres(h)==true) equivale a (MultiploDeTres(h)), ya que el valor de retorno de esa función es un bool.

¿ quiere decir que tiene ese valor "multiplodetres"?
dg> - cuando se escribe:
dg>  bool multiplodetres (int n)
dg> {
dg>      if ( n % 3) return false; else return true;
dg> }
dg> - ¿ En que momento coge el valor "n" de "i".

Cuando declaramos o definimos la función MultiploDeTres, n es un parámetro.
Cuando invocamos a la función MultiploDeTres, i es un argumento.

Es importante tener nombres para cada concepto, y este caso es un ejemplo muy bueno.

El parámetro es un valor indeterminado. Cuando se declara la función, el valor del parámetro no está definido, y en muchos casos, la función debe prever casos para distintos valores del parámetro, ya que tiene que estar preparado para manejar tantos como sea posible prever.

En la definición tomamos el valor de n, independientemente de cuanto valga, y le aplicamos ciertas operaciones. En este ejemplo calculamos el resto de dividir n entre 3.

Ahora bien, antes hemos dicho que una sentencia if evalúa lo que hay entre paréntesis como un valor bool, pero el resto de dividir un entero entre 3 puede tomar tres valores: 0, 1 ó 2.

¿Qué hace el compilador en estos casos? Lo hemos dicho al principio: el compilador busca un operador de cambio de tipo y lo aplica de forma implícita, es decir, convierte el valor obtenido de la operación (n % 3) y le aplica un cambio de tipo a bool.

La norma general para convertir un entero a bool es simple: el valor 0 es false, y cualquier otro valor es true.

Así que si que si el resto de dividir n entre 3 es cero, el contenido del paréntesis se considera falso, y por lo tanto se ejecuta la sentencia del else, y se retorna el valor true.

Si por el contrario, el resto de dividir n entre 3 es 1 ó 2, el contenido del paréntesis se considera verdadero, y se ejecuta la sentencia del if, y se retorna el valor false.

Si el resto es 0 es que n es divisible entre 3, así que es correcto regresar con true, si el resto no es cero (1 ó 2), n no es divisible entre 3, y lo correcto es regresar con false. Así que la función trabaja correctamente.

Podríamos haber simplificado más la función, pero lo consideré excesivo para este ejemplo:

----8<------
bool MultiploDeTres(int n) {
    return !(n%3);
}
----8<------

Te dejo como ejercicio ver por qué esta función es equivalente a la anterior. :)

Ahora bien, volviendo al tema de diferenciar entre argumentos y parámetros.

Cuando invocamos a una función, el valor (o valores) que pasamos son parámetros. Los parámetros sí tienen un valor concreto. Cuando se ejecuta una función, el ordenador sustituye los argumentos por los valores de los parámetros indicados y ejecuta el código de la función.

Se puede considerar que una definición de función es una descripción de cómo funciona, y una llamada (o invocación) es una ejecución con valores concretos para los parámetros.

Cuando llamamos a MultiploDeTres con el valor 14, por ejemplo, el ordenador asigna al parámetro n el valor de argumento, en este caso 14, y ejecuta el código.

14%3 es 2, por lo tanto, distinto de cero, es decir, la condición del if es verdadera, y se ejecuta la sentencia "return false".

dg> - Aprovechando ¿ Para que sirve la lines a continuación de <iostream>la cual pone "using namespace std;"?

Al principio del capítulo se explica por qué se añaden esas líneas. Bueno, no se explica, porque aún no se ha llegado a los conceptos necesarios para comprenderlas.

#include <iostream>
Hace referencia a que se incluya el código necesario para poder usar funciones, tipos y objetos necesarios para realizar salidas por pantalla y entradas desde el teclado. En este caso, para usar "cout" o "endl".

using namespace std;
Hace referencia a que se use un espacio de identificadores con el nombre "std". No hablamos de espacios con nombres hasta el capítulo 26. Se trata de un mecanismo por el cual las variables pueden ser declaradas en distintos entornos, de modo que se pueda usar el mismo nombre para varios objetos sin que se produzcan conflictos. En realidad es como los apellidos. Varias personas se pueden llamar "Fernando", siempre que tengan distinto apellido. En la vida real hay personas con el mismo nombre y apellido, pero en C++ esto no es posible.

De esta forma es posible que, por ejemplo, en un gran programa, distintos programadores no tengan que preocuparse de si otro programador del proyecto ha usado el mismo nombre para una variable, una función o una clase, ya que cada uno puede usar un espacio con nombre diferente.

Lo mismo se aplica a bibliotecas de funciones, que en general se usarán por terceras personas. Imagina que tienes un programa y que usas una biblioteca gráfica que has descargado de internet. Si el que ha diseñado la biblioteca ha usado una función llamada "Mostrar", tu programa no podría tener ninguna función, variable, etc con ese nombre. Y si ya la tenía, te verías obligado a cambiar el nombre en cada aparición en el programa.

Con los espacios con nombre no tienes que preocuparte por esos problemas, siempre que la biblioteca use un espacio con nombre, claro.

Este es el caso de "std". Todas las bibliotecas estándar de C++ definen sus identificadores en el espacio con nombre "std" (estándar).

La sentencia "using namespace std;" indica que se use el espacio "std" como espacio predefinido, de modo que no tengamos que indicarlo en cada caso.

Si no se usa, aún es posible usar objetos como "cout" o métodos como "endl", siempre que añadamos su nombre de espacio de esta forma: "std::cout" o "std::endl". Es decir, con su nombre completo.

dg> No se si me he expresado con claridad, pero no consigo entender la relación. Muchas gracias

Espero haber aclarado algunas dudas, y perdona por la extensión del mensaje.

Hasta pronto.

-- 
Salvador Pozo (Administrador)
mailto:salvador en conclase.net


Más información sobre la lista de distribución Cconclase