60 - Parámetros por valor y por referencia de objetos


Los parámetros de un método pueden ser de tipo objeto y también los podemos hacer por valor o referencia.

Cuando un parámetro de tipo objeto se hace por valor se hace una copia idéntica de dicho objeto en otra parte de memoria.

Si modificáramos los atributos de dicho parámetro no se estarían modificando los atributos del objeto que le pasamos en la llamada.

Veamos un programa que recibe un objeto como parámetro por valor y cambiemos sus atributos y veamos que sucede con el objeto original.

Programa:

#include<iostream>

using namespace std;

class Reloj {
    int hora;
    int minuto;
    int segundo;
public:
    Reloj(int hora, int minuto, int segundo) { this->hora = hora; this->minuto = minuto; this->segundo = segundo; };
    void imprimir();
    void intentoCambiar(Reloj r);
};

void Reloj::imprimir()
{
    cout << hora << ":" << minuto << ":" << segundo << "\n";
}

void Reloj::intentoCambiar(Reloj r)
{
    r.hora = 1;
    r.minuto = 0;
    r.segundo = 0;
}

int main()
{
    Reloj reloj1(10, 10, 10);
    Reloj reloj2(20, 20, 20);
    reloj1.intentoCambiar(reloj2);
    reloj1.imprimir();  // imprime: 10:10:10
    reloj2.imprimir();  // imprime: 20:20:20
    return 0;
}

Este proyecto lo puede descargar en un zip desde este enlace : ObjetoParametroValor1.zip

En la main creamos dos objetos de la clase Reloj e inicializamos la hora, minuto y segundo actual de cada reloj.

El método intentoCambiar recibe un parámetro por valor de la clase Reloj:

class Reloj {
    int hora;
    int minuto;
    int segundo;
public:
    Reloj(int hora, int minuto, int segundo) { this->hora = hora; this->minuto = minuto; this->segundo = segundo; };
    void imprimir();
    void intentoCambiar(Reloj r);
};

En el algoritmo del método intentoCambiar modificamos los atributos del parámetro r:

void Reloj::intentoCambiar(Reloj r)
{
    r.hora = 1;
    r.minuto = 0;
    r.segundo = 0;
}

Pero como podemos comprobar cuando desde la main le enviamos el objeto reloj2 al método intentoCambiar los atributos originales de dicho objeto no se alteran (siguen teniendo los valores que le pasamos al constructor):

int main()
{
    Reloj reloj1(10, 10, 10);
    Reloj reloj2(20, 20, 20);
    reloj1.intentoCambiar(reloj2);
    reloj1.imprimir();  // imprime: 10:10:10
    reloj2.imprimir();  // imprime: 20:20:20
    return 0;
}

Esto funciona así ya que dentro del método intentoCambiar se crea un parámetro r de la clase Reloj con una copia idéntica del objeto que le pasamos desde la main. Es importante hacer notar que si la clase Reloj tiene destructor el mismo se ejecutará cuando se libere el espacio del parámetro r al finalizar el método intentoCambiar.

Modifiquemos este mismo problema y definamos el parámetro por referencia:

Programa:

#include<iostream>

using namespace std;

class Reloj {
    int hora;
    int minuto;
    int segundo;
public:
    Reloj(int hora, int minuto, int segundo) { this->hora = hora; this->minuto = minuto; this->segundo = segundo; };
    void imprimir();
    void intentoCambiar(Reloj &r);
};

void Reloj::imprimir()
{
    cout << hora << ":" << minuto << ":" << segundo << "\n";
}

void Reloj::intentoCambiar(Reloj &r)
{
    r.hora = 1;
    r.minuto = 0;
    r.segundo = 0;
}

int main()
{
    Reloj reloj1(10, 10, 10);
    Reloj reloj2(20, 20, 20);
    reloj1.intentoCambiar(reloj2);
    reloj1.imprimir();  // imprime: 10:10:10
    reloj2.imprimir();  // imprime: 1:0:0
    return 0;
}

Este proyecto lo puede descargar en un zip desde este enlace : ObjetoParametroReferencia1.zip

El único cambio que hemos dispuesto es que el parámetro r pasa por referencia (es decir ahora r es un alias de la variable que le pasamos desde la main):

void Reloj::intentoCambiar(Reloj &r)
{
    r.hora = 1;
    r.minuto = 0;
    r.segundo = 0;
}

Si modificamos los atributos de r, estamos modificando los atributos del objeto reloj2 que le pasamos desde la main y es por eso que cuando imprimimos luego los atributos del objeto reloj2 vemos que los datos son distintos a los que le pasamos en el constructor:

int main()
{
    Reloj reloj1(10, 10, 10);
    Reloj reloj2(20, 20, 20);
    reloj1.intentoCambiar(reloj2);
    reloj1.imprimir();  // imprime: 10:10:10
    reloj2.imprimir();  // imprime: 1:0:0
    return 0;
}

Otra cosa importante que hay que destacar que cuando pasamos un parámetro por referencia el destructor de dicho parámetro no se ejecuta.

Cuando se pasan objetos grandes el tiempo de hacer la copia en el parámetro puede ser costoso para la eficiencia de nuestro programa. Es muy común en estos casos pasar por referencia aunque no tengamos que modificar el objeto que le pasamos, le agregamos el modificador const al parámetro por referencia para evitar modificarlo.

Problemas propuestos

  1. Plantear la clase Reloj y definir como atributo la hora, minuto y segundo. En el constructor inicializar los atributos. Definir un método que retorne si son iguales los atributos de dos objetos de tipo Reloj, el método tiene que tener la estructura:

      bool iguales(Reloj r);
    
  2. Volver a codificar el problema anterior pero ahora al método iguales hacer que llegue como referencia el parámetro (agregamos el modificador const ya que no tenemos que modificar el objeto que le pasamos como referencia):

      bool iguales(const Reloj &r);
    
Solución
#include<iostream>

using namespace std;

class Reloj {
    int hora;
    int minuto;
    int segundo;
public:
    Reloj(int hora, int minuto, int segundo) { this->hora = hora; this->minuto = minuto; this->segundo = segundo; };
    void imprimir();
    bool iguales(Reloj r);
};

void Reloj::imprimir()
{
    cout << hora << ":" << minuto << ":" << segundo << "\n";
}

bool Reloj::iguales(Reloj r)
{
    if (hora == r.hora && minuto == r.minuto && segundo == r.segundo)
        return true;
    else
        return false;
}

int main()
{
    Reloj reloj1(10, 10, 10);
    Reloj reloj2(10, 10, 10);
    reloj1.imprimir();  
    reloj2.imprimir();  
    if (reloj1.iguales(reloj2))
        cout << "Tienen la misma hora los relojes";
    else
        cout << "No tienen la misma hora los relojes";
    return 0;
}

Este proyecto lo puede descargar en un zip desde este enlace :ObjetoParametroValor2.zip

#include<iostream> using namespace std; class Reloj { int hora; int minuto; int segundo; public: Reloj(int hora, int minuto, int segundo) { this->hora = hora; this->minuto = minuto; this->segundo = segundo; }; void imprimir(); bool iguales(const Reloj &r); }; void Reloj::imprimir() { cout << hora << ":" << minuto << ":" << segundo << "\n"; } bool Reloj::iguales(const Reloj &r) { if (hora == r.hora && minuto == r.minuto && segundo == r.segundo) return true; else return false; } int main() { Reloj reloj1(10, 10, 10); Reloj reloj2(10, 10, 10); reloj1.imprimir(); reloj2.imprimir(); if (reloj1.iguales(reloj2)) cout << "Tienen la misma hora los relojes"; else cout << "No tienen la misma hora los relojes"; return 0; }

Este proyecto lo puede descargar en un zip desde este enlace :ObjetoParametroReferencia2.zip

Retornar