[C con Clase] Duda sobre clases

Salvador Pozo salvador en conclase.net
Lun Mayo 20 15:41:15 CEST 2013


Hola:

MAj> ya probado este codigo , el resultado es:
MAj> 2
MAj> 0
MAj> pero no lo tengo claro , es decir personalmente pensaria que , son compilar
MAj> y son probar el codigo al ejecutarlo tendria una respuesta como:
MAj> 1
MAj> 2
MAj> Primero no entiendo como funciona si estoy enviando como parametro un
MAj> numero entero

Empecemos por el principio, pues:

Estás pasando como parámetro un entero, pero la función "f" espera una o dos referencias a objetos de la clase A, por lo tanto, hace una conversión implícita de int a A, y usa el valor por defecto para el segundo parámetro.

En estos casos, los constructores con un parámetro de un tipo cualquiera de las clases se pueden (y generalmente así se hace) considerar como operadores de conversión de tipo.

Por ejemplo, un constructor de una clase X que admita un entero, se puede considerar como un operador de conversión de tipo de entero a X. Otro constructor con un parámetro de tipo float, se considera un operador de conversión de tipo de float a X, etc.

De forma simétrica, un operador que devuelva un entero se puede considerar un operador de conversión de tipo de X a int, pero en ese caso, C++ permite difinir el operador directamente. En tu clase, por ejemplo, podemos definir el operador de conversión de tipo a int de este modo:

operator int() { return mn; }

Y usarlo de este modo:

cout << (int)a << endl;

Observa que no necesitamos indicar el valor de retorno. El operador int siempre devuelve un valor int.

MAj> Tambien , creeria que el unico que implicitamente funciona es el
MAj> constructor copia por que como parametros recibimos : const A &a2 = A()
MAj> Es decir este parametro hace que se ingrese al constructor declarado , peor
MAj> el otro no se como relacionarlo con un entero.

Te estás equivocando con el operador &. Cuando se usa en una declaración de parámetro dentro de una función, le estás diciendo al compilador que lo que estás pasando es una referencia al objeto. Esto no es lo mismo que hacer una copia por referencia. De hecho, es todo lo contrario, estás impidiendo que se haga una copia del objeto.

En este caso: 
void f(const A &a1 , const A &a2= A())...

a1 es una referencia, o lo que es lo mismo, un alias para el objeto que has pasado como primer parámetro. Concretamente, es una referencia constante.

a2 es otra referencia, si la llamada se hace con dos argumentos, es una referencia al segundo. Si se hace con uno sólo, es una referencia al objeto creado en ese mismo momento.

Ahora veamos por qué la salida es 2 y 0.

Evidentemente, es porque se invoca dos veces al constructor con un entero como entrada, y ninguna al constructor copia.

Las dos llamadas al constructor están hechas en la invocación a la función "f", una por cada parámetro.

En el primer parámetro, como llamamos con un entero, se hace una conversión de tipo, de int a A. O si lo prefieres, llamamos al constructor que requiere como parámetro un entero, ya que es el único disponible.

En el segundo parámetro, como no se proporciona un valor, se crea un parámetro con valor por defecto, y se invoca al constructor con un parámetro entero, usando el valor por defecto, que es cero.

El constructor copia se puede invocar explícitamente, haciendo una copia de un objeto existente.

Por ejemplo:

    A a(19);
    A b(a); // Usar el constructor copia

En tu caso, como no hemos definido el operador de asignación, también podemos invocar el constructor copia usando el operador.

    A c=a;  // Usar el constructor copia a través de asignación.

Esto es porque el operador de asignación se define desde el constructor copia por defecto.

Si quieres una función que use el operador copia en sus parámetros, pasa los parámetros por copia, y no por referencia:

void f2(A a1, A a2=A()) {}

    A a(4), b(9);
    f2(a, b);

Quiero hacer notar un hecho curioso. Si invocamos a f2 con uno o dos parámetros enteros, ¿qué crees que pasaría?

    f2(3, 5);

Lo lógico es que se usase el constructor con un parámetro entero para construir cada objeto argumento, y luego el constructor copia para hacer copia de esos argumentos y pasarlos a la función.

Pero si lo pruebas verás que el constructor copia no se invoca en este caso.

El motivo es que el compilador dispone de optimizaciones, y no siempre sigue todos los pasos previsibles según la teoría. En este caso, deduce que no necesita copiar los parámetros, ya que son parámetros por valor, y los parámetros originales son constantes, no es necesario trabajar con una copia de los objetos creados, y prescinde de esa copia.

Hasta pronto.

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


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