590 likes | 734 Views
Informática II. Clase 7: Herencia. Diego Fernando Serna Restrepo. Semestre 2011/2. Chiste del Día. Zona de preguntas. Cuando se llama el constructor de copia? Qué es Poliformismo ? Que es el puntero this ? ¿Cómo se puede diferenciar entre la sobrecarga del operador prefijo y sufijo?.
E N D
Informática II Clase 7: Herencia Diego Fernando Serna Restrepo Semestre 2011/2
Chiste del Día Informática II 2011/2
Zona de preguntas • Cuando se llama el constructor de copia? • Qué es Poliformismo? • Que es el puntero this? • ¿Cómo se puede diferenciar entre la sobrecarga del operador prefijo y sufijo? Informática II 2011/2
Contenido Repaso 1 Herencia 2 Métodos ocultos 3 Funciones Virtuales 4 Informática II 2011/2
Sobrecargado de funciones (Polimorfismo) • Consiste en crear diferentes funciones con el mismo nombre, con el fin de ofrecer al usuario una manera más sencilla de recordar las funciones. //Algunos protototipos intfuncion(int); intfuncion(long, long); intfuncion(long); /* El programa determina cual de las funciones “funcion” ha sido llamada, según los parámetros con que sea llamada la función*/ Informática II 2011/2
Sobrecargado de los métodos de las clases • Los métodos de las clases también pueden ser sobrecargados como el resto de las funciones, su sintaxis es prácticamente igual. classRectangulo { public: // función DibujarFigura() sobrecargada de la clase voidDibujarFigura() const; voidDibujarFigura(intunAncho, intunaAltura) const; private: intsuAncho; intsuAltura; }; voidDibujarFigura() const; voidDibujarFigura(intunAncho, intunaAltura) const; Informática II 2011/2
Inicialización de las variables miembro • Las variables miembro pueden ser inicializadas tanto en la etapa de inicialización de métodos, como en el cuerpo de éstos. //Etapa de inicialización Gato::Gato(): suEdad(5), suPeso(8) { //Cuerpo del constructor } //Nombre y parámetros del constructor Informática II 2011/2
Constructores de copia La copia superficial es buena para casi todo tipo de variables; sin embargo, es muy fácil de romper cuando los miembros son punteros a objetos en el Heap. Informática II 2011/2
Constructores de copia • La solución para evitar problemas ocasionados por las copias superficiales, es usando los constructores de copia para que estos hagan la copia en diferentes espacios del Heap, realizando lo que se denomina como “Copia Profunda”. Informática II 2011/2
Constructores de copia Informática II 2011/2
Constructores de copia Informática II 2011/2
Sobrecargado de los operadores • La sobrecarga de operadores quiere decir que se pueden redefinir algunos de los operadores existentes en C++ para que actúen de una determinada manera, definida por el programador, con los objetos de una clase creada por el mismo. • La sobrecarga de operadores se emplea para declarar “como es que funciona” dicho operador cuando se utilizan sobre los objetos. 2+1=0 Informática II 2011/2
Sobrecarga de operadores • El objetivo de la sobrecarga de operadores es simplificar al máximo el código a escribir, a cambio de complicar algo la definición de las clases. Una clase que disponga de operadores sobrecargados es una clase más compleja de definir, pero más sencilla e intuitiva de utilizar. Informática II 2011/2
Sobrecarga de operadores • La sobrecarga de un operador puede ser declarada de la siguiente forma: tipodeRetornooperatorop (parametros); Void, int, char, … Palabra reservada -, --, + , ++, =, ==, … Void, int, char, … voidoperator ++ (); // Ejemplo de una declaración para el sobrecargado del operador ++ Informática II 2011/2
Ejemplo Sobrecarga del operador Prefijo usingnamespacestd; contador mycontador(); intmain() { mycontador.incrementar(); cout<<"incremento utlizandounmetodo: " \ << mycontador.getvariable()<<endl; ++mycontador; cout<<"incremento sobrecargandoel operador ++:" \ <<mycontador.getvariable()<<endl; return 0; } contador::contador():variable(0){} //constructor de copia contador::contador(contador& original){ variable=original.variable; } int contador::getvariable() const{ returnvariable; } void contador::setvariable(int _variable){ variable=_variable; } void contador::incrementar(){ ++variable; } class contador{ private: int variable; public: contador(); //constructor de copia contador(contador& original); intgetvariable() const; voidsetvariable(int _variable); void incrementar(); //sobrecarga el operador //preincremento (++i) voidoperator ++(); ~contador(); }; 0 1 //definición de la sobrecarga del operador preincremento (++i) void contador::operator++(){ ++variable; } //Destructor contador::~contador(){} Informática II 2011/2
Ejemplo Sobrecarga del operador Prefijo • Ahora lo que generalmente se desea es poder es retornar un objeto tipo contador, de este modo poder reasignarlo a otro objeto tipo contador. //definición de la operador preincremento (++i) contador contador::operator++(){ ++variable; contador temp; temp.setvariable(variable); returntemp; } usingnamespacestd; contador cont1; intmain() { cont1.incrementar(); cout<<"cont1: " <<cont1.getvariable()<<endl; contador cont2=++cont1; cout<<"cont2: "<<cont2.getvariable()<<endl; return 0; } cont1: 1 cont2: 2 Informática II 2011/2
Sobrecargado del operador posfijo • Declaración const Contador Contador::operator++(intflag) { Contador temp(*this); ++itsVal; returntemp; } /* Notese el uso de la variable flag... dicha vble es declarada pero ¡¡nunca es utilizada!! */ flagtipoint tiene valor 0 y no se utiliza nunca, solo sirve como criterio de distinción frente al operador de preincremento. Informática II 2011/2
Sobrecarga del operador de Postincremento De este modo, el valor del objeto será modificado, pero en la expresión donde aparezca se tomará el valor antes de modificar. Informática II 2011/2
Ejemplo Sobrecarga del operador Posfijo //Constructor contador::contador():variable(0){} //constructor de copia contador::contador(contador& original){ variable=original.variable; } int contador::getvariable() const{ returnvariable; } void contador::setvariable(int _variable){ variable=_variable; } void contador::incrementar(){ ++variable; } //definicion de la operador preincremento (++i) const contador& contador::operator++(){ ++variable; return *this; } //definicion del operador posincremento (i++) const contador contador::operator++(intflag){ //hace una copia del objeto contador temp(*this); ++variable; returntemp; } usingnamespacestd; contador cont1; //Ejemplo operador posincremento intmain() { cont1.incrementar(); cout<<"incremento utlizando un metodo: "<< cont1.getvariable()<<endl; contador cont2=++cont1; cout<<"El valor de cont1 luego de preincremento "<<cont1.getvariable()<<endl; cout<<"El valor de cont2: "<<cont2.getvariable()<<endl; cont2=cont1++; cout<<"El valor de cont2: "<<cont2.getvariable()<<endl; cout<<"El valor de cont1 luego de posincremento: "<<cont1.getvariable()<<endl; return 0; } cont1: 1 cont1: 2 cont2: 2 cont2: 2 cont1: 3 class contador{ private: int variable; public: contador(); //constructor de copia contador(contador& original); intgetvariable() const; voidsetvariable(int _variable); void incrementar(); //sobrecarga el operador preincremento(++i) const contador& operator ++(); //sobrecarga del operador posincremento(i++) const contador operator ++(intflag); ~contador(); }; Informática II 2011/2
Sobrecarga de operadores Informática II 2011/2
Operadores Binarios • son aquellos que requieren dos operandos, como la suma o la resta. Cuando se sobrecargan operadores en el interior se asume que el primer operando es el propio objeto de la clase donde se define el operador. Debido a esto, sólo se necesita especificar un operando como argumento. Informática II 2011/2
Sobrecargando el operador + usingnamespacestd; //Ejemplo operador + intmain() { contador cont1(5),cont2(6); contador cont3=cont1+cont2; cout<<"el valor de cont1 es: "<<cont1.getvariable()<<endl; cout<<"el valor de cont2 es: "<<cont2.getvariable()<<endl; cout<<"el valor de cont3 es: "<<cont3.getvariable()<<endl; return 0; } contador::contador():variable(0){} contador::contador(int _variable): variable(_variable){} //constructor de copia contador::contador(contador& original){ variable=original.variable; } int contador::getvariable() const{ return variable; } void contador::setvariable(int _variable){ variable=_variable; } void contador::incrementar(){ ++variable; } cont1: 5 cont2: 6 cont3: 11 class contador{ private: int variable; public: contador(); contador(int _variable); //constructor de copia contador(contador& original); intgetvariable() const; voidsetvariable(int _variable); void incrementar(); //sobrecarga el operador + contador operator +(const contador& other); ~contador(); }; //definición del operador + contador contador::operator +(const contador& other){ contador temp; temp.setvariable(variable+other.getvariable()); returntemp; } contador::~contador(){} Informática II 2011/2
Sobrecargando el operador = int main() { contador cont1(5),cont2(6); cont2=cont1; cout<<"el valor de cont1 es: "<<cont1.getvariable()<<endl; cout<<"el valor de cont2 es: "<<cont2.getvariable()<<endl; return 0; } • Qué pasa si el objeto cont1 tiene como variables miembro punteros que acceden a memoria dinámica? • El objeto cont2 al ser de la misma clase que cont1 igualmente ya cuenta con espacio en memoria dinámica, luego solo basta con copiar el nuevo valor respectivo en dicho espacio. • Qué pasa con los datos originales almacenados por cont2? Informática II 2011/2
Sobrecargando el operador = //sobrecarga el operador = constcontador& operator =(constcontador& other); //definiciondeloperador = constcontador& contador::operator =(constcontador& other){ if(this==&other){ return *this; } else{ variable=other.variable; } return *this; } Informática II 2011/2
Contenido Repaso 1 Herencia 2 Métodos ocultos 3 Funciones Virtuales 4 Informática II 2011/2
Herencia Informática II 2011/2
Herencia Informática II 2011/2
Herencia • La clase de la que se parte en este proceso recibe el nombre de clase base, y la nueva clase que se obtiene se denomina clase derivada. Ésta a su vez puede ser clase base en un nuevo proceso de derivación, iniciando de esta manera una jerarquía de clases. Informática II 2011/2
Clase Base Abstracta La herencia presenta múltiples ventajas, como la posibilidad de reutilizar código sin tener que escribirlo de nuevo. Esto es posible porque todas las clases derivadas pueden utilizar el código de la clase base sin tener que volver a definirlo en cada una de ellas. Son Clases que no tiene otra utilidad que la de ser clase basepara otras clases se deriven de ella. Informática II 2011/2
Declaración de Herencia • Cuando se declara una clase, es posible indicar de donde va a heredar sus carácterísticas, y se declara de la siguiente manera: classclaseDerivada : tipoDeAccesoclaseBase Ejemplo: class perro : public animal { ……. } // Se genera la clase perro a partir de la clase animal Informática II 2011/2
Modificador de acceso Protected • Cuando un miembro de una clase es declarado como “protected”, se garantiza que dicho miembro puede ser accedido tanto para dicha clase como desde sus clases derivadas. • Para el resto de los casos no podrá ser accedida directamente (como los miembros privados). Informática II 2011/2
Modificador de acceso Protected • Cuando se usa herencia pública, se garantiza que todos sus miembros protegidos podrán ser accedidos desde las clases derivadas. • Si un miembro es declarado como “private”, no podrá ser accedido por sus clases derivadas. • Los miembros públicos seguirán siendo públicos en toda la línea de herencia. Informática II 2011/2
Modificadores de Acceso Informática II 2011/2
Ejemplo Herencia //clase vehiculo vehiculo::vehiculo():vel(0),on(false){ cout<<"constructor clase base"<<endl; } vehiculo::vehiculo(int _vel,bool _on):vel(_vel),on(_on){ cout<<"constructor con argumentos clase base"<<endl; } intvehiculo::getVel() const { returnvel; } voidvehiculo::setVel(int _vel){ vel=_vel; } vehiculo::~vehiculo(){} //clase carro carro::carro():Ncambio(0),acelerar(0){ cout<<"constructor por defecto clase derivada"<<endl; } void carro::cambCambio(int _cambCambio){ Ncambio= _cambCambio; } carro::~carro(){} classvehiculo{ protected: intvel; boolon; public: vehiculo(); vehiculo(int _vel,bool _on); intgetVel() const; voidsetVel(int _vel); ~vehiculo(); }; usingnamespacestd; carro audi; intmain() { //damos un valor a una variable miembro heredada audi.setVel(10); //accedemos a un metodo heredado cout<<audi.getVel()<<endl; return 0; } audivel: 10 class carro: publicvehiculo{ private: intNcambio; int acelerar; public: carro(); voidcambCambio(int _Ncambio); ~carro(); }; class bicicleta: privatevehiculo{ private: intNcambio; int pedalear; intNdiscos; public: bicicleta(); voidcambCambio(int _Ncambio); ~bicicleta(); }; Informática II 2011/2
Notas: Informática II 2011/2
Constructores y Destructores • Cuando un objeto derivado es creado primero es llamado el constructor de la clase base y luego el propio. intvel; bool on; Parte vehículo Objetocarro intNcambio; int acelerar; Informática II 2011/2
Pasar argumentos al constructor base • Se pueden inicializar las variables miembro heredadas, llamando al constructor de la clase base desde la etapa de inicialización del constructor de la clase derivada así: carro(int _vel,bool _on); carro::carro(int _vel,bool _on): Ncambio(0), acelerar(0){ } vehiculo(_vel,_on), Informática II 2011/2
Redefinición de funciones • Es posible que una clase derivada “sobreescriba” el funcionamiento de un método de su clase base, basta con redefinir nuevamente dicho método en la clase derivada. • Para poder “sobreescribir ” un método es necesario que las firmas de ambas clases sean iguales • Nota: Firma = a todo lo declarado en el prototipo de las funciones (incluyendo const) a excepción de su variable de retorno. intgetVel() const; Firma Argumento deretorno Informática II 2011/2
Ejemplo Redefinición de Métodos classvehiculo{ protected: intvel; boolon; public: vehiculo(); vehiculo(int _vel,bool _on); intgetVel() const; voidsetVel(int _vel); ~vehiculo(); }; class carro: publicvehiculo{ private: intNcambio; int acelerar; public: carro(); carro(int _vel,bool _on); intgetVel() const; voidcambCambio(int _Ncambio); ~carro(); }; intvehiculo::getVel() const { cout<<"funciongetVel clase base"<<endl; returnvel; } int carro::getVel() const{ cout<<"funciongetVel clase derivada"<<endl; returnvel; } intmain() { //damos un valor a una variable miembro heredada audi.setVel(10); //accedemos a un método heredado redefinido cout<<audi.getVel()<<endl; //acedemos al metodo de la clase base cout<<audi.vehiculo::getVel()<<endl; return 0; } Informática II 2011/2
Sobrecargar Vs Redefinir Informática II 2011/2
Contenido Repaso 1 Herencia 2 Métodos ocultos 3 Funciones Virtuales 4 Informática II 2011/2
Ocultando los Métodos de la Clase Base Informática II 2011/2
Ejemplo classvehiculo{ protected: intvel; boolon; public: vehiculo(); vehiculo(vehiculo &V); vehiculo(int _vel,bool _on); voidsetVel(int _vel); intgetVel() const; //sobrecarga metodogetVel intgetVel(floatother); intgetVel(intother); virtual vehiculo* clonar(); virtual ~vehiculo(); }; carro audi(10,true); intmain() { int a=5; float b=45.6; //solo podemos acceder a el método heredado redefinido audi.getVel(); audi.getVel(a); //para haceder a los demás métodos sobrecargados audi.vehiculo::getVel(); audi.vehiculo::getVel(a); audi.vehiculo::getVel(b); return 0; } • cuando se redefine un método sobrecargado, todas sus variaciones son escondidas. Si no se desea que se oculten, entonces se debe redefinir cada una de las variaciones o sobrecargas en la clase derivada. class carro: publicvehiculo{ private: intNcambio; int acelerar; public: carro(); carro(int _vel,bool _on); intgetVel() const; voidcambCambio(int _Ncambio); ~carro(); }; Informática II 2011/2
Conversiones entre clase Base y Derivada Informática II 2011/2
Conversiones entre clase Base y Derivada Objeto_claseBase= Objeto_claseDerivada; Objeto_claseDerivada= Objeto_claseBase; • Se puede hacer referencia a un objeto de la clase derivada con su dirección contenida en un puntero a la clase base. vehiculo *ptrV=new carro(30,true); carro *ptrC=newvehiculo(); Informática II 2011/2
Conversiones entre clase Base y Derivada usingnamespacestd; intmain() { vehiculo *ptrV=new carro(30,true); cout<< ptrV->getVel()<<endl; //se accede al metodo de la clase base return 0; } Informática II 2011/2
Contenido Repaso 1 Herencia 2 Métodos ocultos 3 Funciones Virtuales 4 Informática II 2011/2
Métodos Virtuales • Tenemos un problema cuando deseamos acceder al método de la clase derivada a través de un puntero de tipo clase base. Para lograrlo debemos definir el método como virtual dentro de la clase base. • Para declarar una función como virtual basta con adicionar la palabra “virtual” al inicio de su prototipo. virtual par.retornodeclaracionMetodo(parámetros entrada ); Informática II 2011/2
classvehiculo{ protected: intvel; boolon; public: vehiculo(); vehiculo(int _vel,bool _on); virtual intgetVel() const; voidsetVel(int _vel); ~vehiculo(); }; class carro: publicvehiculo{ private: intNcambio; int acelerar; public: carro(); carro(int _vel,bool _on); intgetVel() const; voidcambCambio(int _Ncambio); ~carro(); }; Ejemplo Métodos Virtuales intvehiculo::getVel() const { cout<<"funciongetVel clase base"<<endl; returnvel; } int carro::getVel() const{ cout<<"funciongetVel clase derivada"<<endl; returnvel; } usingnamespacestd; intmain() { vehiculo *ptrV=new carro(30,true); cout<<ptrV->getVel()<<endl; //se accede al método de la clase derivada return 0; } Informática II 2011/2
Métodos Virtuales Informática II 2011/2