C++ PUNTEROS a clases POO

Iniciado por ianmorar03, 6 Septiembre 2018, 10:10 AM

0 Miembros y 2 Visitantes están viendo este tema.

ianmorar03

Tengo un problema a la hora de hacer un programa:
Digamos que quiero insertar objetos dentro de una colección, y en el main, quiero hacer una copia de un objeto a otro, a la hora de cambiar los atributos del nuevo objeto(copia), se cambian los de la original, doy un ejemplo:
Código (cpp) [Seleccionar]

#include <iostream>
using namespace std;
class persona{
private:
   string nom;
   string id;
public:
   persona(){
       nom=" ";
       id=" ";
   }
   persona(string n,string i){
       nom = n;
       id=i;
   }
   string getNom(){
       return nom;
   }
   string getId(){
       return id;
   }
   void setNom(string n){
       nom=n;
   }
   void setId(string i){
       id=i;
   }
   ~persona(){
       nom=" ";
       id=" ";
   }
};
class coleccion{
private:
   persona **p;
   int cantidad;
public:
   coleccion(){
       p=new persona*[10];
       cantidad=0;
   }
   void ingresarPersona(persona *pe){
       p[cantidad]=pe;
       cantidad++;
   }
   persona devuelve(int i){
       return *p[i];
   }
   ~coleccion(){
       for(int i=0;i<cantidad;i++){
           delete persona[i];
       }
       delete[] persona;
   }

};

int main(){
   coleccion *c = new coleccion;
   persona *p1 = new persona("JUAN","12345");
   persona *p2 =new persona("PEDRO","45678");

   c->ingresarPersona(p1);
   c->ingresarPersona(p2);

   cout<<c->devuelve(0).getNom()<<endl; //muestra por pantalla "JUAN"
   cout<<c->devuelve(1).getNom()<<endl; //muestra por pantalla "PEDRO"

   persona *p3; //hago una tercera persona
   *p3 = c->devuelve(0); //le asigno los valores de la p1 a la p3
   c->insertarPersona(*p3);

   cout<<c->devuelve(2).getNom()<<endl; //muestra por pantalla "JUAN"

   c->devuelve(2).setNom("CAMBIO");

   //se supone que solo cambia el nombre de p3
   //pero cambia tambien el valor de p1

   cout<<c->devuelve(0).getNom()<<endl; //muestra por pantalla "CAMBIO"


   system("PAUSE");
   return 0;
};




Cuando yo hago una copia del puntero, al cambiar los valores de la copia, cambian los de la original, como puedo solucionar este problema?
Espero me hayan entendido :D
Aquellos que estan locos como para pensar que pueden cambiar el mundo , son los que lo hacen.

CalgaryCorpus

#1
Asi funcionan los punteros. Son guardadores de las direcciones de memoria de los objetos.
El operador * y el operador -> no hacen mas que visitar las direcciones de memoria, por lo que cuando haces una copia de los punteros, solo haces copia de las direcciones, no de los objetos.

Por la misma razon, cuando modificas usando el operador -> estas visitando el mismo objeto para los 2 punteros.

Tener 2 punteros apuntando al mismo objeto se llama "alias" y esto que te ocurre es indicador de lo que ya te he explicado.

Posible solucion: Crea un constructor de copia y usalo en vez de hacer una asignacion en las lineas 69 y 70.

EL constructor de copia es un nuevo constructor que recibe un objeto del mismo tipo que la clase que estas creando.

Finalmene, despues de implementar el constructor de copia y reemplazar esas 2 lineas, tu codigo se veria:

Código (cpp) [Seleccionar]
persona *p3 = new persona(c->devuelve(0));

Sin ningun otro cambio necesario, deberia funcionar.

Una convencion que no estas usando es nombrar las clases con mayuscula inicial, intenta seguirlo para hacer mas facil a otros leer tu codigo.
Aqui mi perfil en LinkedIn, invitame un cafe aqui

MAFUS

He intentado ejecutar tu código para ayudarte pero, aparte de la falta del constructor copia, faltan arreglar unos cuantos errores. No debería compilarte.

Para saber qué es y qué hace un constructor copia pásate por aquí: http://c.conclase.net/curso/?cap=029#P29_COPIA

Aunque he de decirte que, ya que tus objetos están instanciados mediante new, en vez de recibir una referencia en el argumento (que también deberías incluir este constructor), deberás recibir un puntero.

ianmorar03

Voy a averiguar mas sobre un constructor de copia y vere si funciona
Las clases siempre las uso en mayúsculas, y este código ni siquiera se
si compila, lo hice lo mas rapido y sencillo para que me entendieran :P
El codigo original es mas complicado, y tiene mucho mas cosas gg

Pero muchas gracias de antemano a los 2, @MAFUS y @CalgaryCorpus ;-)
Aquellos que estan locos como para pensar que pueden cambiar el mundo , son los que lo hacen.

MAFUS

Desde mi ignorancia de C++ hice este código:

Código (c++) [Seleccionar]
#include <iostream>

using namespace std;

class persona {
private:
    string nom;
    string id;

public:
    persona(){
        nom="";
        id="";
    }

    persona(string n, string i) : nom(n), id(i) {}

    persona(const persona &p) : nom(p.nom), id(p.id) {} // Este copia sirve para objetos en stack

    persona(const persona *p) : nom(p->nom), id(p->id) {} // Este copia sirve para objetos en heap

    string getNom() {
        return nom;
    }

    string getId(){
        return id;
    }

    void setNom(string n){
        nom = n;
    }

    void setId(string i){
        id = i;
    }
};

class coleccion{
private:
    persona *p[10];
    int cantidad;

public:
    coleccion() {
        cantidad=0;
    }

    void ingresarPersona(persona *pe) {
        if(cantidad<10) {
            p[cantidad]=pe;
            cantidad++;
        }
    }

    persona* devuelve(int i) {
        return p[i];
    }

    ~coleccion() {
        for(int i=0; i<cantidad; i++){
            delete p[i];
        }
    }
};

int main() {
    coleccion *c = new coleccion;
    persona *p1 = new persona("JUAN","12345");
    persona *p2 = new persona("PEDRO","45678");

    c->ingresarPersona(p1);
    c->ingresarPersona(p2);

    for(int i=0; i<2; ++i)
        cout<<"c->devuelve("<<i<<")->getNom() = "<<c->devuelve(i)->getNom()<<"\n";
    cout<<endl;

    persona *p3;
    p3 = new persona(c->devuelve(0));
    c->ingresarPersona(p3);

    for(int i=0; i<3; ++i)
        cout<<"c->devuelve("<<i<<")->getNom() = "<<c->devuelve(i)->getNom()<<"\n";
    cout<<endl;

    c->devuelve(2)->setNom("CAMBIO");

    for(int i=0; i<3; ++i)
        cout<<"c->devuelve("<<i<<")->getNom() = "<<c->devuelve(i)->getNom()<<"\n";
    cout<<endl;
}

ianmorar03

#5
Ahora que recuerdo, creo haber visto el constructor de copia
en clases y no estaba prestando atención jaja
Utilizare el de copia para dinamico, que estoy utilizando
Dinamicos en mi programa
Aquellos que estan locos como para pensar que pueden cambiar el mundo , son los que lo hacen.

Mr.Moonlight

Siempre que estés trabajando con una clase que use punteros , los más recomendable es crear siempre constructor de copia y sobrecargar el operador de asignación para evitar este tipo de problemas o al menos yo siempre lo entendí así  :P