Practica5

=**Práctica 5: Herencia**=

**1. Introducción**
La herencia es un mecanismo para reutilizar código. Lo mejor es explicar esto con un ejemplo. Imaginemos que queremos en un juego (tipo Sim) manejar animales como el perro, gato y pájaro. Cada animal tiene un nombre y fecha de nacimiento. Pero además, el perro puede ladrar, el gato maullar y el pájaro cantar.

La primera opción para implementar es crear tres clases distintas. Por ejemplo, la declaración de gato sería algo así: code format="cpp" using namespace std; using namespace animals;
 * 1) include
 * 2) include 
 * 3) include

int main(int argc,char **argv) {

time_t timenow; time(&timenow); Cat cat("hugo",timenow,"largo"); cat.setName("hugito"); cat.miauu; } code

code format="cpp" #define _Cat_H_ #include #include using namespace std; namespace animals{ /**\brief This represents a cat */   class Cat{ public: /**Empty constructor */           Cat; /**Parametrized constructor */           Cat(string name,time_t dateOfBirth); /**Copy constructor */           Cat(const Cat &C); /**Destructor */           ~Cat; /**Returns the name of the cat */           string getName; /**Returns the date of birth */           time_t getDateOfBirth; /**Special ability */           void miauu; private: string _name; time_t _dateOfBirth; };   }    #endif code Ahora, si desearamos hacer el perro, tendríamos algo idéntico excepto por la función miauu. Igualmente para el pájaro.
 * 1) ifndef _Cat_H_

La herencia nos permite ahorrar trabajo en este tipo de situaciones. Para ello, crearemos una clase Animal que tendrá todos los elementos comunes a las tres clases. Sea Animal de la siguiente forma: code format="cpp" using namespace std; namespace animals{ /**\brief Generic class for animals class Animal { public: /**Empty constructor */   Animal{} /**Parametrized constructor *@param name of the animal *@param dateOfBirth of the animal */   Animal(string name,time_t dateOfBirth){ _name=name; _dateOfBirth=dateOfBirth; }   /**Copy constructor *@param A element to be copy of   */ Animal(const Animal & A){ _name=A._name; _dateOfBirth=A._dateOfBirth; }   /**Destructor */   ~Animal{} /**Returns the name of the animal */   string getName{return _name;} /**Returns the dato of bith of the animal */   time_t getDateOfBirth{return _dateOfBirth;} /**Sets the name */   void setName(string name){_name=name;} /**Sets date of birth */   void setDateOfBirth(time_t dateOfBirth){_dateOfBirth=dateOfBirth;}
 * 1) ifndef _Animal_H_
 * 2) define _Animal_H_
 * 3) include
 * 4) include

private: string _name; time_t _dateOfBirth; };

} code Bueno, antes de seguir vamos a detenernos un momento en algo nuevo que tenemos aquí: la clase string. Como podéis haber observado, tenemos la inclusión del archivo y después hemos usado la clase string. Esta es una clase definida en el estándar de C++ para el manejo de cadenas. Es una forma fácil y sencilla de manejar cadenas. Veamos un ejemplo de uso: code format="cpp" int main {   string cad; cad="hola, hoy es un buen dia"; cout<<cad<<endl; string cad2=cad; cout<<cad2<<endl; string cad3="hola"; cout<<cad3<<endl; } code Además de eso, tiene funciones para buscar en la cadena: code format="cpp" int main {       string cad="hola como estas"; //busca como int pos=cad.find("hola"); if(pos==-1) cout<<"No esta"<<endl; else cout<<"Si esta y comienza en la posicion="<<pos<<endl; }
 * 1) endif

code La concatenación es también fácil: code format="cpp" int main {       string cad="Hola, como estas"; cad=cad+"?\n"; cad+="Pues yo bien, gracias\n"; string cad2="Estas seguro?\n"; cad=cad+cad2+"Que si, no seas pesado!\n"; cout<<cad<<endl; } code

Tras el inciso, volvemos a donde estábamos. La clase Animal tiene los elementos básicos para un animal. Ahora, lo ideal es coger esta clase y ampliarla para que represente un gato o perro o pájaro. Para ello, los que hacemos es crear una clase Cat que hereda de la clase Animal. Al heredar, la clase Cat tendrá todos los métodos y variables de la clase Animal y podrá agregar otros. Veamos el fichero cat.h code format="cpp" using namespace std; namespace animals {
 * 1) ifndef _Cat_H_
 * 2) define _Cat_H_
 * 3) include 
 * 4) include

/**\brief A cat class Cat: public Animal { public: /**   */    Cat; /**   */    Cat(string name,time_t dateOfBirth,string typeOfHair); /**   */    Cat(const Cat &C); /**   */    ~Cat;

/**Returns the cat's type of hair */   string getTypeOfHair{return _typeOfHair;} /**Sets the type of hair */   void setTypeOfHair(string typeOfHair){_typeOfHair=typeOfHair;} /**Makes the cat to do miauu */   void miauu{cout<<"Miauuuuu"<

namespace animals {

/////////////////////////////// // // /////////////////////////////// Cat::Cat : Animal {

} /////////////////////////////// // // /////////////////////////////// Cat::Cat(string name,time_t dateOfBirth,string typeOfHair) : Animal(name,dateOfBirth) { _typeOfHair=typeOfHair; } /////////////////////////////// // // /////////////////////////////// Cat::Cat(const Cat &C): Animal(C) { _typeOfHair=C._typeOfHair; } /////////////////////////////// // // /////////////////////////////// Cat::~Cat {

} };

code Bueno, que tenemos por aquí. En la declaración, tras el nombre de la clase, se indica de qué clase hereda esta. code format="cpp" class Cat: public Animal code En este caso, lo que indicamos es que Cat hereda de Animal de forma pública. También se dice que Cat //es un// Animal y que Animal es la clase padre de Cat. Cuando se hereda de forma pública se heredan todos los miembros y variables públicas y protegidas. Sin embargo, no se puede acceder a los elementos privados de la clase padre.

Cuando se hereda de forma pública, podemos hacer algo como: code format="cpp" using namespace std; using namespace animals;
 * 1) include
 * 2) include 
 * 3) include

int main(int argc,char **argv) {

time_t timenow; time(&timenow); Cat cat("hugo",timenow,"largo"); cat.setName(“hugito”); cat.miauu; } code

Es decir, podemos usar setName que está definida en Animal. Ahora analizamos el cat.cpp. code format="cpp" Cat::Cat : Animal {

}

code Lo que tenemos a la derecha es la llamada al constructor de la clase Animal. Es decir, el constructor de la clase que hereda debe llamar al constructor de la clase padre. Igualmente, en

code format="cpp" Cat::Cat(string name,time_t dateOfBirth,string typeOfHair) : Animal(name,dateOfBirth) { _typeOfHair=typeOfHair; }

code estamos llamando al constructor parametrizado. Démonos cuanta de que estamos usando los parámetros que tenemos a la izquierda. El constructor parametrizado de Animal copia los datos name y dateOfBirth,mientras que en esta función se copia _typeOfHair. Finalmente,

code format="cpp" Cat::Cat(const Cat &C): Animal(C) { _typeOfHair=C._typeOfHair; }

code el constructor de copia de Cat llama al constructor de copia de Animal pasando un Cat como parámetro. Esto es algo nuevo. Si miramos el código del constructor de copia de Animal veremos que recibe como argumento un const Animal &A. ¿Por que nos acepta entonces que le pasemos un Cat? Bien, el compilador sabe que Cat //es un// Animal y hace un casting. A esto se le llama //upcasting// o casting hacia arriba. La razón es que convertimos una clase en su padre. También es posible hacer el inverso aunque de forma un tanto especial que no veremos aún.

**2. Más tipos de Herencia**
La herencia puede ser de diversos tipos. En C++ podemos realizar //herencia múltiple//. Esto quiere decir que una clase puede heredar de más de un padre, heredando todas las funciones de todos los padres. Ejemplo: Un gato es un animal y es un mamífero code format="cpp" class Cat : public Animal, public Mamal {   ....    }; code

En la sección anterior, hemos visto la herencia pública. De esta forma, el que hereda deja accesible los miembros de su padre.

Herencia privada es cuando la clase hija puede hacer uso de todas la parte pública de su padre. Sin embargo, cuando alguien usa la clase hija, no puede usar los miembros del padre. Veamos un ejemplo con una lista de contactos simple. Sea la clase Contact (contact.h):

code format="cpp" namespace contacts { class Contact { public: /**   */    Contact{} /**   */    Contact(string name,int phone) {       _name=name; _phone=phone; }   /**    */    Contact(const Contact & C)    { _name=C._name; _phone=C._phone; }   /**    */    void setName(string name) {       _name=name; }
 * 1) ifndef _Contact_H_
 * 2) define _Contact_H_

/**   */    string getName {       return _name; }

/**   */    void setPhone(int phone) {       _phone=phone; }   /**    */    int getPhone {       return _phone; }

/**   */    Contact & operator=(const Contact &C) {       _name=C._name; _phone=C._phone; return *this; }

/**   */    bool operator==(const Contact &C) {       return _name==C._name && _phone==C._phone; }

/**   */    void print {       cout<<"Contact Name="<<_name<<" Phone="<<_phone< {   public: /**       */        ContactList{ _nContacts=0;//number of contacts currently in the list resize(5); //Maximum number of initial contact }
 * 1) ifndef _Contact_List_H_
 * 2) define _Contact_List_H_
 * 3) include 
 * 4) include
 * 5) include 

/**       */        ~ContactList{}

/**Adds a contact to the list if it is not yet */       void add(const Contact &C) {           if(!isContact(C)){ //See if we reached the maximum if (_nContacts==Vector::size) { //yes, resize the vector //first, keep a copy Vector copy=(*this); resize(size+5); for(unsigned int i=0;i::size;i++) if ((*this)[i]==C) return true; return false; }

/**Indicates the number of contacts in the list */       int size{return _nContacts;} /**       */        void print {           for(unsigned int i=0;i<size;i++) {               (*this)[i].print; }       }    private: int _nContacts; };

};

code Ahora el main.cpp
 * 1) endif

code format="cpp" using namespace contacts; int main(int argc,char **argv) {   ContactList CL; CL.add(Contact("pepe",12398)); Contact C1("maria",9312); CL.add(C1); CL.add(Contact("juan",212398)); CL.add(Contact("carmen",2398)); CL.add(Contact("pedro",2398)); cout<<"N contacts="<

Daros cuenta de que estamos usando el  de la practica anterior. Copiarlo a este directorio para usarlo. Finalmente os dejo el makefile code all:contactlist

contactlist:main.cpp contactlist.h contact.h vector.h   g++ -I. main.cpp -o contactlist clean: rm contactlist *~ -f

code Vamos a analizar el código. En primer lugar tenemos la clase Contact, que representa un contacto. Después creamos la clase ContactList que es un vector de contactos. Esta clase, hereda de forma privada de Vector lo que le permite almacenar fácilmente la información sin tener que preocuparse demasiado por crear y liberar memoria.

Fijese que en la clase ContactList hemos redefinido la función size. En esta ocasión significa el número de contactos y no el número de elementos en el vector. Al hacer esto, cuando llamamos a la función size dentro del código de ContactList, el compilador tiene que decidir a cual de las dos llamar. Por defecto, el compilador llama a la de la clase actual si hay ambigüedad. Vease ContactList.print. Pero si queremos llamar a la función size de Vector tenemos que indicarlo de forma explicita de la siguiente forma NombreClase::nombre_funcion.

Cuando en el main creamos un objeto de la clase ContactList, la clase Vector queda oculta. Por tanto en el main, no podemos usar el operador [] por ejemplo. Esto sería incorrecto: code format="cpp" using namespace contacts; int main(int argc,char **argv) {
 * 1) include 

ContactList CL; CL[0]=Contact("jose",12398); }

code Se dice que los miembros de Vector no son accesibles desde ContactList


 * 3. Ejercicios**

3.1 Agregue a la clase ContacList la función del(const Contact &C) que elimina un contacto de la lista. Al borrar el contacto, deberá desplazar todos los elementos que hay detrás de él para poder mantener la consistencia en el vector.

3.2 Vamos a crear una jerarquía de clases para crear un programa simple de dibujo. Para ello vamos a crear elementos que puedan ser dibujados en pantalla: Square, Circle. Para ello, crearemos una clase base que llamaremos Shape y que pertenece al espacio de nombres shapes. La clase Shape tendrá los siguientes elementos privados: a) int _x, _y : representan la posición del objeto b) int _color: representa el color del objeto La clase Shape tendrá las siguientes funciones miembro públicas a) Constructor vacío y de copia. No se hará el parametrizado. b) void getPosition(int &x,int &y): pone en x,y la posición del objeto. c) void setPosition(int x,int y): fija la posición del objeto d) void setColor(int c); e) int getColor; f) void draw; Esta función deberá imprimir por pantalla la posicion y color del objeto cout<<"x,y="<<_x<<" "<<_y<<" color="<<_color<<endl;

Tras crear la clase Shape, cree la clase Square y Circle que también pertenecen al espacio de nombres shapes y heredan de Shape de forma pública.

La clase Square tendrá los siguientes elementos privados: a) int _squareSize que representa el tamaño

La clase Square tendrá las siguientes funciones miembro públicas: a) void setSquareSize(int) b) int getSquareSize;

La clase Circle tendrá los siguientes elementos privados: a) int _radius que representa el radio

La clase Circle tendrá las siguientes funciones miembro públicas: a) void setRadius(int) b) int getRadius;

Use el siguiente código para probar code format="cpp" using namespace shapes; int main(int argc,char **argv) { Circle C; C.setPosition(10,10); C.setColor(1231); C.setRadius(1); C.draw; Square S; S.setPosition(13,13); S.setColor(31); S.setSquareSize(2); S.draw; } code
 * 1) include 
 * 2) include 