Foros del Web » Programación para mayores de 30 ;) » C/C++ »

[SOLUCIONADO] Tengo una duda con los punteros en los atributos

Estas en el tema de Tengo una duda con los punteros en los atributos en el foro de C/C++ en Foros del Web. Buenas verán, me surgió una duda sobre cual es la diferencia cuando se crea una clase con los atributos declarados como punteros y cuando no ...
  #1 (permalink)  
Antiguo 09/04/2015, 19:49
Avatar de Cardo2095  
Fecha de Ingreso: marzo-2015
Ubicación: En mi casa
Mensajes: 18
Antigüedad: 9 años, 1 mes
Puntos: 0
Pregunta Tengo una duda con los punteros en los atributos

Buenas verán, me surgió una duda sobre cual es la diferencia cuando se crea una clase con los atributos declarados como punteros y cuando no es así, este es un ejemplo de una clase abstracta y ademas me gustaría saber cuando se deben hacer los atributos como punteros, si es en una clase abstracta o si también en una clase normal


Código PHP:
class Animal{

protected:

    
int *id;//estos como punteros
    
char *mes;

    
int annoNac;//estos normales
    
char tamaño;
    
int peso;
public:
        
Animal(int,char,int,char,int);


//quiero saber si este constructor esta bien declarado o cambia en algo con los punteros

Animal::Animal(int,char,int,char,int){ 
    
this->id=id;
    
this->mes=mes;
    
this->annoNac=annoNac;
    
this->tamaño=tamaño;
    
this->peso=peso;
    

  #2 (permalink)  
Antiguo 10/04/2015, 00:05
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Tengo una duda con los punteros en los atributos

Un puntero es un tipo de variable que tiene la capacidad de acceder a posiciones aleatorias de la memoria o, dicho de otra forma, de referenciar otras variables.

Para poder realizar asignaciones correctamente es necesario que el tipo de la variable que está a la derecha de la igualdad sea compatible con el tipo de la variable que está a la izquierda. Unos ejemplos:
Código C:
Ver original
  1. int a, b;
  2. a = b; // Ok
  3.  
  4. int c;
  5. char d;
  6. c = d; // Ok, son de diferente tipo pero la conversión directa es posible
  7.  
  8. char* e, f:
  9. e = f; // Ok
  10.  
  11. char* g;
  12. char h;
  13.  
  14. g = &h; // Ok, con el '&' estamos obteniendo la dirección de memoria de h
  15. g = h; // Error, 'g' es un puntero y 'h' no.

En tu caso, las asignaciones que haces de 'id' y 'mes' son incorrectas porque el constructor recibe los parámetros por valor y tu estás intentando asignar un valor a un puntero... cuando un puntero únicamente espera recibir direcciones de memoria.

Un puntero en una clase, como norma general tiene sentido si:
  • El valor o el objeto almacenado se va a compartir entre varios elementos (y te interesa, por cuestion de memoria o porque necesitas que todos accedan al mismo valor, usar un puntero en vez de una copia del objeto)
  • El tipo de la variable representa un objeto ciertamente pesado (que ocupa bastante memoria)
  • Cuando tengas que usar listas de elementos de tamaño indeterminado y no puedas usar los contenedores propios de C++
  • Para reducir los tiempos de compilación al reducir las dependencias en los archivos de cabecera:
    • Cada vez que cambia un archivo de cabecera se tienen que recompilar todo el código que referencie directa o indirectamente a dicha cabecera.
    • Cada include obliga al compilador a cargar el archivo que se indica y esto pasa en cascada (los includes que están dentro de los includes también hay que cargarlos). Mover todos los includes posibles a los cpp mejora los tiempos de compilación.

Yo te recomendaría que SIEMPRE empezases por definir los miembros sin usar punteros. Si resulta que alguno de esos miembros necesita ser un puntero te darás cuenta porque te resultará imposible o complicado realizar determinadas tareas. Cuando adquieras más experiencia irás viendo las ventajas e inconvenientes de tener los miembros por valor o con punteros.

PD.: Una clase abstracta es aquella que no se puede construir por tener algúna función o método declarada como virtual pura. En este caso no vas a ser capaz de crear un elemento de esta clase ya que "está incompleta". En tu caso nada te impide crear elementos de tipo "Animal", no tienes ningún método definido como virtual puro, por lo que tu clase no va a ser, en ningún caso, abstracta.

Un saludo.
  #3 (permalink)  
Antiguo 10/04/2015, 20:06
Avatar de Cardo2095  
Fecha de Ingreso: marzo-2015
Ubicación: En mi casa
Mensajes: 18
Antigüedad: 9 años, 1 mes
Puntos: 0
Información Respuesta: Tengo una duda con los punteros en los atributos

Gracias por tu ayuda amigo.

- me quedo una duda con el constructor que me dijiste xq no me da el error que dijiste sino que el IDE si lo asignaba como bueno, usa visual c++ 2010,

-y además me preguntaba si a la hora de crear un objeto, por ejemplo:
quiero crear perro pero nose si debo crear tambien el objeto animal y asignarselo a perro o que.

lo que tengo que hacer es una lista y crear muchos perros, pero nose si debo crear una lista para animal o solo la del perro, debido a que nose si a la hora de asignar los atributos a perro solo se crea perro o si tambien debo crear animal.
algo así

suponiendo ya tengo la clase animal y también perro, te paso este pequeño ejemplo para saber si esta bien declarado o no;

Código PHP:
Animal *an;

Perro *p;
p=new Perro;


AgregarPerro(){

    
cout<<"Ingrese el tamano"<<endl
    
cin>>tamano//tamaño es atributo de animal
    
an->setTamano(tamano);  //que pasa si lo asigno al objeto p en lugar del objeto an?
              
                    
    
cout<<"Ingrese la raza"<<endl
    
cin>>raza;   //raza es atributo de perro
    
p->setRaza(raza); 


//xq se debe hacer de esta forma suponiendo que es correcto

     
ListaAnimal *La = new listaAnimal(); 
      
La->InsertarPerro(*an); 

      
ListaPerro *Lp = new ListaPerro(); 
      
Lp->Insertar(*p); 


  #4 (permalink)  
Antiguo 11/04/2015, 02:44
Avatar de vangodp  
Fecha de Ingreso: octubre-2013
Mensajes: 934
Antigüedad: 10 años, 6 meses
Puntos: 38
Respuesta: Tengo una duda con los punteros en los atributos

Código C++:
Ver original
  1. #include <iostream>
  2.  
  3. class Animal { //clase que va heredar todos animales
  4.     public:
  5.         // Solo poner metodos comunes entre todos animales, por ejemplo no todos animales saben ladrar, asi que ladrar no va aqui, pero todo animal tiene peso, id, nombre...
  6.         void setNombre ( std::string nombre ) { this->nombre = nombre; } //se usa para dar nombre al animal
  7.         std::string getNombre () const { return nombre; }   //se usa para obtener nombre del animal
  8.         void setId ( int *id ) { this->id = id; } //se usa para apuntar a un id que relacionaremos al animal animal
  9.         int *getId() const { return this->id; }  //se usa para obtener un id relacionado al animal
  10.        
  11.     private:
  12.    
  13.     protected:
  14.         int *id;
  15.         int annoNac;
  16.         double tamanio;
  17.         double peso;
  18.         std::string nombre;
  19. };
  20.  
  21. class Perro: public Animal {  //perro tiene todos los miembros de Animal. Se puede decir que perro es un animal =)
  22.     public:
  23.         //Los perros pueden ladrar asi que aqui si ponemos ladrar
  24.         std::string comunicar() { return "uau uau!"; }
  25.     private:
  26.    
  27. };
  28.  
  29. int main () {
  30.     //Esto es para crear un solo perro
  31.     Perro unPerro;
  32.     //puntero
  33.     int id = 47062; //numero que representa el id del perro...Perro apuntará ese entero que será su ID
  34.     unPerro.setId ( &id ); //hacemos que perro apunte el id... No hay sentido ya que el propio Animal deberia albergar el ID, pero es solo un ejemplo de como debe hacer con punteros.
  35.     unPerro.setNombre ( "Toby" );
  36.     std::cout << "Tengo un perro que se llama " << unPerro.getNombre() << " Y sabe decir: " << unPerro.comunicar() << std::endl;
  37.     std::cout << "El ID de "<< unPerro.getNombre() <<  " es: " << *unPerro.getId() << std::endl;
  38.    
  39.     //Esto seria para crear a muchos perros
  40.     Perro *ptrPerro = new Perro[10];
  41.     // Se verifica que exista el apunte a algo
  42.     if ( !ptrPerro ){ std::cout << "Error en la asignacion" << std::endl; return 1; }
  43.    
  44.     //si llegamos aqui es que tenemos el arreglo de perros, nombramos al primer perro
  45.     ptrPerro[0].setNombre("Chicho");
  46.     std::cout << "\nEl nombre del primer perro del arreglo es: " << ptrPerro[0].getNombre() << std::endl;
  47.    
  48.     //destruimos el array de perros
  49.     delete [] ptrPerro;
  50.     std::cin.ignore();
  51.     return 0;
  52. }

A ver... aquí hay todo lo que buscas creo. Solo adapta a lo que buscas y intenta entenderlo.

no necesitas instanciar la clase animal. Perro debe heredar de animal o bien debes crear un objeto Animal dentro de perro.

No tengo mucha practica con clases tampoco pero bueno XD.. Suerte
  #5 (permalink)  
Antiguo 12/04/2015, 14:58
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Tengo una duda con los punteros en los atributos

Cita:
Iniciado por Cardo2095 Ver Mensaje
- me quedo una duda con el constructor que me dijiste xq no me da el error que dijiste sino que el IDE si lo asignaba como bueno, usa visual c++ 2010
Que un compilador concreto sepa sortear tu error no quiere decir que lo estés haciendo bien... puede que simplemente sean casualidades de la vida y eso no evita que en el futuro o bajo otras condiciones el programa empiece a fallar.

Cita:
Iniciado por Cardo2095 Ver Mensaje
-y además me preguntaba si a la hora de crear un objeto, por ejemplo:
quiero crear perro pero nose si debo crear tambien el objeto animal y asignarselo a perro o que.
Si tu tienes una clase "Perro", que hereda de otra "Animal" y tu quieres crear una instancia de "Perro", únicamente tienes que preocuparte de crear un "Perro".

Cuando una clase hereda de otra los constructores de la clase hija, por defecto, van a llamar al constructor por defecto de la clase padre. Si necesitas que un constructor de la clase hija llame a un constructor concreto de la clase padre tienes que indicarlo explícitamente:

Código C++:
Ver original
  1. class Animal
  2. {
  3.   public:
  4.     Animal( )
  5.     { std::cout << "Animal( )" << std::endl; }
  6.  
  7.     Animal( const Animal& )
  8.     { std::cout << "Animal( Animal )" << std::endl; }
  9. };
  10.  
  11. class Perro : public Animal
  12. {
  13.   public:
  14.  
  15.     // Este constructor llamará al constructor por defecto de "Animal"
  16.     Perro( )
  17.    { std::cout << "Perro()" << std::endl; }
  18.  
  19.     // Fíjate que aquí llamamos al constructor copia de "Animal"
  20.     Perro( const Perro& perro ) : Animal( perro )
  21.     { std::cout << "Perro( Perro )" << std::endl; }
  22. };
  23.  
  24. int main( )
  25. {
  26.   std::cout << "------" << std::endl;
  27.   Perro perro;
  28.   std::cout << "------" << std::endl;
  29.   Perro perro2( perro );
  30.   std::cout << "------" << std::endl;
  31. }

Pero claro, estas llamadas entre constructores solo tiene que tenerlas en cuenta el que programa los constructores. Es algo totalmente transparente para el que usa las clases.

Cita:
Iniciado por Cardo2095 Ver Mensaje
lo que tengo que hacer es una lista y crear muchos perros, pero nose si debo crear una lista para animal o solo la del perro, debido a que nose si a la hora de asignar los atributos a perro solo se crea perro o si tambien debo crear animal.
Cuando tu tienes una clase base y una o varias hijas, puedes hacer algo tal que:

Código C++:
Ver original
  1. // aunque la variable "animal" es de tipo "Animal", realmente almacena un perro
  2. // Esto es lo que se llama polimorfismo
  3. Animal* animal = new Perro;
  4.  
  5. // Esta línea dará error porque "Animal" no hereda de "Perro"
  6. Perro* perro = new Animal;

Lo que sucede es que el polimorfismo es una característica de nivel medio en C++, tiene sus riesgos y es mejor controlar primero la base antes de lanzarse a este tipo de prácticas.

Si aun así decides usar polimorfismo, te recomiendo usar siempre punteros, si no vas a perder información:

Código C++:
Ver original
  1. class Animal
  2. {
  3.   public:
  4.     Animal( )
  5.     { std::cout << "Animal( )" << std::endl; }
  6.  
  7.     Animal( const Animal& )
  8.    { std::cout << "Animal( Animal )" << std::endl; }
  9. };
  10.  
  11. class Perro : public Animal
  12. {
  13.   public:
  14.  
  15.     Perro( )
  16.    { std::cout << "Perro()" << std::endl; }
  17.  
  18.     Perro( const Perro& perro ) : Animal( perro )
  19.     { std::cout << "Perro( Perro )" << std::endl; }
  20. };
  21.  
  22. int main( )
  23. {
  24.   std::cout << "------" << std::endl;
  25.   Perro perro;
  26.   std::cout << "------" << std::endl;
  27.   // Fíjate que aquí no se llama al constructor de perro
  28.   // Lo que acaba de suceder es que has creado un "Animal" a partir de un "Perro"
  29.   // pero no has creado un nuevo "Perro"
  30.   Animal animal( perro );
  31.   std::cout << "------" << std::endl;
  32. }

Luego, aparte de esto tienes que tener en cuenta la sobrecarga de operadores, los métodos virtuales, destructores virtuales y otra serie de conceptos que si no dominas bien te van a frustrar demasiado.

Cita:
Iniciado por vangodp Ver Mensaje
Código C++:
Ver original
  1. void setId ( int *id ) { this->id = id; } //se usa para apuntar a un id que relacionaremos al animal animal
No tiene demasiado sentido, en este caso, que id sea un puntero... como ejemplo para demostrar que se puede, vale. Pero mejor no complicarse... cuando un elemento necesite ser un puntero tranquilo que te darás cuenta porque no podrás hacer las cosas usando variables "por valor".

Cita:
Iniciado por vangodp Ver Mensaje
Código C++:
Ver original
  1. unPerro.setId ( &id ); //hacemos que perro apunte el id... No hay sentido ya que el propio Animal deberia albergar el ID, pero es solo un ejemplo de como debe hacer con punteros.
Y aquí está el peligro de usar punteros y referencias... ¿qué sucede si "id" es una variable local? Cuando salgamos de la función la variable se perderá y el puntero se quedará apuntando a algo que ya no existe. Aquí esto no sucede porque es la variable, aunque local, está en el main, pero me sirve para ilustrar el ejemplo.

Cita:
Iniciado por vangodp Ver Mensaje
Código C++:
Ver original
  1. Perro *ptrPerro = new Perro[10];
  2.     // Se verifica que exista el apunte a algo
  3.     if ( !ptrPerro ){ std::cout << "Error en la asignacion" << std::endl; return 1; }
Si falla el new se va a lanzar una excepción... tu código solo va a funcionar como pretendes si modificas la línea del new tal que:

Código C++:
Ver original
  1. Perro *ptrPerro = new (std::nothrow) Perro[10];

Pero sin ese "nothrow" el new va a lanzar excepciones.
  #6 (permalink)  
Antiguo 12/04/2015, 17:10
Avatar de vangodp  
Fecha de Ingreso: octubre-2013
Mensajes: 934
Antigüedad: 10 años, 6 meses
Puntos: 38
Respuesta: Tengo una duda con los punteros en los atributos

Cita:
Perro *ptrPerro = new (std::nothrow) Perro[10];
Gracias por la aclaración =)

Como dije.. Aun estoy verde en lo que a clases y también memoria dinámica se refiere. A ver si poco a poco llego allá XD
  #7 (permalink)  
Antiguo 13/04/2015, 00:16
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Tengo una duda con los punteros en los atributos

Cita:
Iniciado por vangodp Ver Mensaje
Gracias por la aclaración =)

Como dije.. Aun estoy verde en lo que a clases y también memoria dinámica se refiere. A ver si poco a poco llego allá XD
No te preocupes. Te lo comenté para que lo supieses... no para corregirte ;)

Etiquetas: atributos, clase, constructor, puntero, punteros
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 11:56.