La descarga está en progreso. Por favor, espere

La descarga está en progreso. Por favor, espere

Algoritmos y Estructuras de Datos

Presentaciones similares


Presentación del tema: "Algoritmos y Estructuras de Datos"— Transcripción de la presentación:

1 Algoritmos y Estructuras de Datos
Introducción al C++

2 Paradigmas de la Programación
Programación procedural Enfatiza el aspecto algorítmico Programa = Algoritmos + Datos

3 Paradigmas de la Programación
La programación procedural en lenguajes previos al C ( BASIC ,FORTAN ) tenían problemas organizacionales debido a que se usaban sentencias de salto (GO TO) que modificaban el flujo normal del programa haciéndolos muy difíciles de seguir. -Programación spaghetti -Write-only code

4 Paradigmas de la Programación
En respuesta a este problema se crearon lenguajes que definían un estilo mas disciplinado llamado programación estructurada. En este caso los saltos se limitan a construcciones o estructuras perfectamente definidas (while ,do while ,for y el if-else) EXPECT PARADIGM SHIFTS 1965 2000

5 Paradigmas de la Programación
Otro principio que también se incorporo es el llamado TOP-DOWN design. La idea básica detrás de este principio era dividir un problema grande en mas pequeños. Hasta terminar en programas mas pequeños llamados módulos. C tiene soporte para este paradigma mediante unidades de programación llamadas funciones

6 Paradigmas de la Programación
En la programación modular se puso énfasis en la organización de los datos mas que en el aspecto algorítmico del programa. En este caso el paradigma era : “Decida que modulos necesita y particione el programa de manera que los datos queden ocultos en dichos modulos”

7 Paradigmas de la Programación
Ej STACK : 1- Crear una interfase para el usuario por ej: push() y pop() . 2- Asegurarse que la implementación del stack (pej: un array de elementos) puede accederse solo a través de dicha interfase. 3- Asegurarse que el stack sea inicializado antes de su primer uso. 4- Proveer un mecanismo para la destrucción del stack cuando no se mas usado.

8 Paradigmas de la Programacion
Ej.: STACK La interfase del stack puede ser definida en stack.h como : // User Interface void push(char); char pop(); const int stack_size =100;

9 Paradigmas de la Programación
La implementación del stack puede ser definida en stack.c como : // Stack implementation #include “stack.h” static char v[stack_size]; (static  LOCAL!!!!) static char *p=v; // Stack inicialmente vacío push(char c) { } // si hay lugar guardo un elemento char pop(){ } // si no esta vacío saco un elemento

10 Paradigmas de la Programacion
La programación modular lleva a la centralización de datos relacionados con un tipo dado, lo que es una notable mejora respecto de los paradigmas previos. El problema es que para cada tipo se debe crear un mecanismo distinto para crear una ”variable “ . Ademas estas “variables” no gozan de los mecanismos que tienen los tipos incorporados en el lenguaje (int, float,etc) como ser la visibillidad (scope) la duracion, pasaje de argumentos ,etc.

11 Paradigmas de la Programacion
Lenguajes como el C++ y ADA permiten al usuario definir tipos que se comportan casi de la misma forma que los tipos nativos del lenguaje. Estos tipos son también conocidos como ADT (Abstract Data Types) o directamente tipos definidos por el usuario El paradigma ahora se convierte en: “Decida que tipo de datos necesita y provea un conjunto completo de operaciones para dicho tipo”

12 Paradigmas de la Programacion
Los números complejos son un ejemplo de tipo definido por el usuario. En el caso del numero complejo debemos definir entre otros: Como se crea un complejo Las operaciones que queremos realizar sobre ellos La destrucción del mismo

13 Primera Clase !!! En el caso de los números complejos se puede usar una struct para definirlo. De esta forma uno podría crear destruir y manipular complejos. Es decir que uno podría modelar OBJETOS usando estructuras y escribir código que opere sobre ellas . Es esto lenguaje orientado a objetos. ? Desafortunadamente no pues el paradigma (OOP) brinda soporte adicional como ser la encapsulacion,el polimorfismo y la herencia que no son aplicables a struct. El C++ brinda algo que se comporta de la misma manera que una struct pero que tiene propiedades adicionales y es conocida como Clase.

14 Primera Clase !!! typedef struct { double x; double y; }Complejo; Class Ccomplejo public: double m_x; double m_y; }; La clase Ccomplejo se comporta igual que la estructura Complejo Data members

15 Clases Una clase es un tipo definido por el usuario
Así como una estructura es un molde así lo es una clase De la misma forma que creamos una variable usando estructuras lo hacemos con las clases. Cuando creamos una variable de una clase dada decimos que creamos un objeto o que creamos una instancia de la clase Ccomplejo z1; // z1 es un objeto tipo Ccomplejo (declaración) Los datos miembros pueden ser cualquiera de los tipos conocidos (float,int, arrays) o tipos definidos por el usuario Además de los datos la clase puede contener funciones que operan sobre estos últimos a estas funciones se las conoce como funciones miembro Los nombres usados dentro de una clase son locales a ella.

16 Clases:Control de acceso
Public: Acceso total. Private: No se pueden acceder desde fuera de la clase Protected: Se vera mas adelante Por omision todos los miembros son privados. Class Ccomplejo { double m_x; public: double m_y; }; Ccomplejo z1; z1.m_x =3; Invalido z1.m_y =3; OK

17 Clases:Ejemplo Creo 2 instancias de CBox
class CBox // Class definition at global scope { public: double m_Length; // Length of a box in inches double m_Breadth; // Breadth of a box in inches double m_Height; // Height of a box in inches }; int main(void) CBox Box1; // Declare Box1 of type CBox CBox Box2; // Declare Box2 of type CBox } Creo 2 instancias de CBox

18 Clases:Ejemplo int main(void) {
CBox Box1; // Declare Box1 of type CBox CBox Box2; // Declare Box2 of type CBox double volume = 0.0; // Store the volume of a box here Box1.m_Height = 18.0; // Define the values Box1.m_Length = 78.0; // of the members of Box1.m_Breadth = 24.0; // the object Box1 Box2.m_Height = Box1.m_Height - 10; // Define Box2 Box2.m_Length = Box1.m_Length/2.0; // members in Box2.m_Breadth = 0.25*Box1.m_Length; // terms of Box1

19 Clases:Funciones miembro
class CBox // Class definition at global scope { public: double m_Length; // Length of a box in inches double m_Breadth; // Breadth of a box in inches double m_Height; // Height of a box in inches double Volume(void) return m_Length*m_Breadth*m_Height; } };

20 Clases:Funciones miembro
El acceso a las funciones miembro se realiza de la misma forma que cuando se accede a un dato miembro double volume = 0.0; volume = Box1.Volume(); // Calculate volume of Box1 cout << endl << "Volume of Box1 = " << volume; cout << endl << "Volume of Box2 = " << Box2.Volume(); No se puede invocar a una función miembro sin especificar el nombre del objeto

21 Clases:Funciones miembro
No es necesario definir la funcion dentro de la clase se puede definir afuera.Solo basta con definir el prototipo de la funcion dentro de la clase y la definicion fuera de la misma. class CBox // Class definition at global scope { public: double m_Length; // Length of a box in inches double m_Breadth; // Breadth of a box in inches double m_Height; // Height of a box in inches double Volume(void); // Prototipo de la funcion };

22 Clases:Funciones miembro
double Cbox::Volume(void) { return m_Length*m_Breadth*m_Height; } El operador :: (operador resolución de entorno) le indica al compilador a quien pertenece la función. No existe ninguna diferencia en cuanto al comportamiento del programa si en como el compilador trata a la función.

23 Clases: Funciones “Inline”
Cuando una función es declarada “inline” el compilador reemplaza su llamada por su definicion. Toda función definida dentro de una clase es inline por defecto (declaración inline implícita). Una declaración explicita seria: Inline double Cbox::Volume(void) { return m_Length*m_Breadth*m_Height; }

24 Clases: Constructores
Cuando se tienen varias clases y con muchos miembros datos la inicialización de los mismos puede ser engorrosa y peligrosa pues se pueden omitir algunas de ellas. Además los datos miembros privados no son accesibles desde el exterior. C++ provee una función implícita en cada clase que se llama constructor por defecto. Esta función es invocada cada vez que se crea un objeto. El nombre de esta función es el mismo que la clase. Ccomplex( ) { }

25 Clases: Constructores
Uno puede definir su propio constructor para inicializar los miembros de datos dentro de la clase este constructor es explicito. CBox(double lv, double bv, double hv) // Constructor definition { cout << endl << "Constructor called."; m_Length = lv; // Set values of m_Breadth = bv; // data members m_Height = hv; }

26 Clases:Constructores
En el siguiente ejemplo se crean 3 objetos dos de ellos están inicializados. CBox Box1(78.0,24.0,18.0); // Declare and initialize Box1 of type CBox CBox Box2; // Declare Box2 - no initial values CBox CigarBox(8.0,5.0,1.0);

27 Clases: Constructores
Cuando el usuario define un constructor el constructor por defecto desaparece pues se asume que el usuario provee todos los constructores necesarios. Por lo tanto al compilar el programa con la definición previa se genera un error pues no encuentra el constructor por defecto para el segundo objeto. Es el usuario quien debe proveerlo En el siguiente ejemplo se agrego el constructor por defecto. CBox(double lv, double bv, double hv) // Constructor definition { cout << endl << "Constructor called."; m_Length = lv; // Set values of m_Breadth = bv; // data members m_Height = hv; } // Default constructor definition CBox( ) { cout << endl << "Default constructor called."; }

28 Sobrecargando Funciones
Algo que puede llamar la atención es que tenemos dos funciones constructoras con el mismo nombre. Como sabe el compilador a cual invocar? Simplemente se fija en el tipo y numero de argumentos con que se invoca a la función. Esto se conoce con el nombre de sobrecarga de funciones( función overloading) por ejemplo supongamos que necesitamos una función que encuentre el máximo de dos valores. Esos valores pueden ser double o int . El nombre mas apropiado para la función seria max(valor1 ,valor2). En C tendríamos que usar una ADT o nombres diferentes pej.: maxint , maxdouble etc.. Con la sobrecarga de funciones podemos evitarnos este problema pues es el compìlador quien toma la decisión de cual usar. Ej: int max(int a , int b) { ………. } double max(double a , double b) { …….. }

29 Sobrecargando Funciones
Ejemplo: #include<iostream.h> int max(int,int); double max(double,double); main() { double a; a=max(10,11); cout << a << endl; a=max(10.1,11.2); } int max(int a,int b) cout << endl << "Int max="; return((a>b)?a:b); double max(double a,double b) cout << endl << "Flt max="; Int max=11 Flt max=11.2

30 Sobrecargando Funciones
En otras palabras la sobrecarga de funciones nos permite re-usar un nombre independientemente del tipo que de los operadnos. Encontrar el máximo de dos valores es una operación muy común para muchos tipos de datos como ser floats doubles ints ,etc.,… Un problema al que uno se enfenta es que el codigo de la funcion para cada tipo de dato se repite. Por ejemplo el codigo para encontrar el maximo de enteros y floats es exactamente igual la unica diferencia es el tipo. C++ provee una solucion para este problema que se conoce como moldes para funciones (function templates).

31 Templates para Funciones
template <class Any > void swap (Any &a , Any &b) { Any temp; temp=a; a=b; b=temp; } La primera línea sirve para que el compilador sepa que estamos preparando un molde y que el tipo genérico se llamara Any (muchos libros usan T o T1 ). Este molde NO genera código solo es una directiva para que el compilador sepa como definir la función. Cuando invocamos a la función swap el compilador se fija en los tipos que usamos y crea la funcion correspondiente. Por ejemplo si usamos enteros en la linea de argumentos el compilador reemplazara Any por int.

32 Templates para Funciones
Los templates pueden tener mas de un tipo genérico template <class Any1,class Any2 > Y también se pueden sobrecargar!!! template <class Any> // swap int, floats,etc void swap(Any &a, Any &b); template <class Any> void swap(Any *a, Any *b, int arraysize); // swap array elements 1 1 Se deja al alumno como ejercicio la implementación de esta función.

33 Templates para Funciones
Ejemplo #include<iostream.h> template <class T> T max(T a,T b); // Prototipo main() { double a; a=max(14,11); cout << endl << a; a=max(15.1,11.3); return(0); } template <class T> T max(T a,T b) // Definicion return((a>b)?a:b); 14 15.1

34 Valores por defecto para constructores
Se pueden especificar valores por defecto en el caso de los constructores por ejemplo: CBox( double lv=1.0, double bv=1.0, double hv=1.0 ) { cout << endl << "Constructor called."; m_Length = lv; // Set values of data members m_Breadth = bv; m_Height = hv; } En este caso debe eliminarse el constructor por defecto pues colisiona con esta ultima definición creando una ambigüedad. En efecto si se crea un objeto como: Cbox CigarrBox; El compilador no puede distinguir entre el constructor por defecto y el ultimo constructor .

35 Valores por defecto para constructores
Una forma alternativa de hacer lo mismo es mediante lo que se conoce como lista de inicialización: CBox (double lv=1.0, double bv=1.0, double hv=1.0) : m_Length (lv), { m_Breadth(bv), m_Height (hv) cout << endl << "Constructor called."; } Esta forma de inicializar es necesaria en ciertos casos particulares y solo se puede usar para constructores. Estos casos son: Cuando el miembro dato es const. y no estático. Cuando el miembro dato es una referencia. Importante: Recordar que los miembros de una clase son inicializados en el orden en que fueron declarados no en el orden en que aparecen en la lista de inicialización.

36 Miembros privados de la clase
La posibilidad de definir miembros privados de una clase nos permite separar la implementación de la misma de su interfase. Clase Datos Miembros Públicos Funciones Miembros Publicas (servidor) Datos Miembros Privados Funciones Miembros Privadas Función Externa a la clase (cliente) Control de acceso

37 Miembros privados de la clase
class CBox // Class definition at global scope { public: CBox(double lv=1.0, double bv=1.0, double hv=1.0) // Constructor definition cout << endl << "Constructor called."; m_Length = lv; // Set values of m_Breadth = bv; // data members m_Height = hv; Servicio } double Volume() // Function to calculate the volume of a box return m_Length*m_Breadth*m_Height; private: double m_Length; // Length of a box in inches double m_Breadth; // Breadth of a box in inches double m_Height; // Height of a box in inches };

38 Miembros privados de la clase
Con este cambio no se puede acceder a los datos miembros desde afuera. Esto permite el ocultamiento de los mismos protegiéndolos de un acceso inadvertido (y los detalles de la implementación en el caso de las funciones privadas). Ahora la única forma de acceso es a través del constructor o funciones publicas que hacen a la interfase del objeto. Declarar a los miembros datos como privados puede parecer una medida algo extrema pero lo único que queremos es protegerlos de una modificación inadvertida desde el exterior. Esto no nos impide tener acceso de lectura a los mismos si creamos una interfaz publica como la siguiente: inline double Cbox:: GetLength(void) { return (m_Lenght)} ; len = Box2. GetLength() ;

39 Haciendo Amigos En algunas ocasiones es necesario que algunas funciones que NO pertenecen a la clase tengan acceso a los miembros de la clase. A este grupo de funciones se las conoce como funciones amigas de la clase. Este grupo de funciones no son miembros de la clase por lo tanto no gozan de todos los privilegios que tienen las funciones miembros. Simplemente son funciones comunes que gozan de ciertos privilegios. Concretamente las funciones amigas pueden acceder a todos los miembros privados de la clase pero no pueden usar directamente el nombre del miembro sino que deben especificar el objeto al que pertenecen de la misma manera que lo haría una función regular excepto salvo que esta ultima no puede acceder a los miembros privados de la clase.

40 Haciendo Amigos La definición de estas funciones puede o no estar en la clase y en caso de no estarlo debe declararse el prototipo dentro de la misma. La declaración del prototipo debe estar precedida por la palabra friend. Ej.: friend double BoxSufrace (CBox aBox); El posicionamiento dentro de la definición de la clase puede hacerse en cualquier lugar de la misma dado que los atributos de acceso (private o public) no son aplicables a las funciones amigas pues no son miembros de la clase. Sin embargo es aconsejable poner la declaración de las funciones amigas después de todas las declaraciones publicas y privadas. Además si bien la función es de acceso global es aconsejable no poner la definición de la misma dentro de la clase sino solamente el prototipo pues después de todo no es una función miembro y podría complicar la lectura de la definición de la clase.

41 Haciendo Amigos class CBox // Class definition at global scope {
public: CBox(double lv=1.0, double bv=1.0, double hv=1.0) // Constructor definition cout << endl << "Constructor called."; m_Length = lv; // Set values of m_Breadth = bv; // data members m_Height = hv; } double Volume() // Function to calculate the volume of a box return m_Length*m_Breadth*m_Height; private: double m_Length; // Length of a box in inches double m_Breadth; // Breadth of a box in inches double m_Height; // Height of a box in inches friend double BoxSurface(CBox aBox); //Friend function };

42 Haciendo Amigos cout << endl La definición de la función es:
// friend function to calculate the surface area of a Box object double BoxSurface(CBox aBox) { return 2.0*(aBox.m_Length*aBox.m_Breadth + aBox.m_Length*aBox.m_Height + aBox.m_Height*aBox.m_Breadth); } Obsérvese que el acceso a los miembros es el mismo que haría una función regular es decir Objeto.miembro. Lo mismo ocurre durante la invocación, no se hace referencia al objeto: cout << endl << "Surface area of Match = " << BoxSurface(Match);

43 Constructores copiadores
Supongamos que declaramos e inicializamos un objeto perteneciente a la clase CBox de la siguiente manera: CBox Box1(78.0, 24.0, 18.0); CBox Box2 = Box1; // Initialize Box2 with Box1 Lo que queremos hacer es crear e inicializar un segundo objeto Box2 a partir del primero. La invocación seria cout << endl << "Box1 volume = " << Box1.Volume() << endl << "Box2 volume = " << Box2.Volume();

44 Constructores copiadores
El programa hace exactamente lo esperado, el volumen de ambos objetos es el mismo: Constructor called. Box1 volume = 33696 Box2 volume = 33696 Pero el constructor fue invocado una sola vez para la creación de Box1. Como fue creado Box2 ? . El mecanismo para la creación del segundo objeto es similar a la que teníamos cuando no definíamos ningún constructor para la clase: el compilador suministraba uno por defecto. En este caso ocurre lo mismo, el compilador suministra un constructor copiador por defecto llamado constructor copiador. Este constructor realiza lo mismo que haríamos nosotros es decir crea el nuevo objeto haciendo una copia miembro a miembro .

45 El puntero “THIS” Cuando invocamos a la función volume() accedíamos a los miembros de la clase Cbox en forma directa usando sus nombres: double Volume() // Function to calculate the volume of a box { return m_Length*m_Breadth*m_Height; } Todo objeto del tipo Cbox tiene estos miembros pero como se sabe a cual de los objetos creados pertenecen? Pues bien cuando se invoca a una función miembro de un objeto dado dicha función recibe un puntero oculto llamado this que apunta a dicho objeto. Entonces cuando se escribe: return m_Length*m_Breadth*m_Height; En realidad es: return thism_Length* thism_Breadth* thism_Height;

46 El puntero “THIS” En este ultimo caso el uso del puntero this fue explicito. Se puede usar por ejemplo este puntero en forma explicita cuando se desea devolver un puntero al objeto actual. Supongamos que deseamos comparar dos objetos del tipo cbox en base a su volumen. Entonces podemos poner: // Function to compare two boxes which returns TRUE (1) // if the first is greater than the second, and FALSE (0) otherwise int compare(CBox xBox) { return this->Volume() > xBox.Volume(); } if( Cigar.compare (Match) )

47 El puntero “THIS” El main completo seria: int main(void) {
CBox Match(2.2, 1.1, 0.5); // Declare Match box CBox Cigar(8.0, 5.0 ,1.0); // Declare Cigar box if(Cigar.compare(Match)) cout << endl << "Match is smaller than Cigar"; else << "Match is equal to or larger than Cigar"; cout << endl; return 0; }

48 El puntero “THIS” Nótese que la función compare podría haberse escrito haciendo uso implícito del puntero this : int compare(CBox xBox) { return Volume( ) > xBox.Volume( ); } Si la función compare hubiese sido externa no tendría acceso al puntero this y por lo tanto habría que escribirla como: int compare(CBox Box1,CBox Box2) { return Box1.volume( ) > Box2. volume( ) }

49 Arrays de objetos // Constructor definition
Podemos declarar arrays de objetos de la misma manera que lo hacemos con los tipos nativos (float, int, etc.). Cada elemento del array provocara una llamada al constructor por defecto. // Constructor definition CBox(double lv, double bv=1.0, double hv=1.0) { cout << endl << "Constructor called."; m_Length = lv; // Set values of m_Breadth = bv; // data members m_Height = hv; } // Default constructor CBox() { cout << endl << "Default constructor called."; m_Length = m_Breadth = m_Height = 1.0; }

50 Arrays de objetos Supongamos que creamos un array de cinco cajas y una caja de cigarros. int main(void) { CBox Boxes[5]; // Array of CBox objects declared CBox Cigar(8.0, 5.0 ,1.0); // Declare Cigar box cout << endl << "Volume of Boxes[3] = " << Boxes[3].Volume() << endl << "Volume of Cigar = " << Cigar.Volume(); cout << endl; return 0; }

51 Arrays de objetos Entonces la salida será:
Default constructor called. Constructor called. Volume of Boxes[3] = 1 Volume of Cigar = 40 CBox Boxes[5]; CBox Cigar(8.0, 5.0 ,1.0); En el caso del array se llamo al constructor por defecto mientras que en el caso la caja de cigarros se llamo al constructor que recibe tres argumentos.

52 Miembros estáticos de una Clase
Tanto los miembros dato como las funciones miembros se los puede definir como estáticos dentro de una clase. Las implicancias de esta definición van un poco mas allá de cuando se definían fuera de la misma Miembros dato estáticos Cuando un miembro dato de una clase se declara como estático se obtiene como resultado que solo una instancia del mismo existe para todos los objetos creados. Esta instancia puede ser accedida por todos ellos. En otras palabras dicha instancia será compartida por ellos

53 Miembros datos estáticos
Un uso posible de esta definición podría ser para contar el numero de objetos de dicha clase fueron creados, es decir cuantas instancias de una clase dada tenemos. La definición de dicho contador se puede hacer en la sección publica de la clase como sigue: class CBox // Class definition at global scope { public: static int ObjectCount; // Count of objects in existence …..

54 Miembros datos estáticos
Surge ahora un problema : Donde inicializamos al miembro dato ObjectCount?. No podemos hacerlo dentro de la definición de la clase pues es simplemente un molde. Tampoco en el constructor pues queremos usar a este ultimo para incrementar el contador cada vez que creamos un objeto nuevo. No podemos hacerlo dentro de una función miembro pues esta ultima esta asociada con un objeto y lo que nosotros en realidad queremos es inicializarla antes de la existencia de cualquier objeto. La respuesta es hacerlo fuera de la definición de la clase con la siguiente sentencia: int CBox::ObjectCount = 0; // Initialize static member of class CBox

55 Miembros datos estáticos
Nótese que la definición static no figura en esta ultima sentencia. Sin embargo obsérvese que debemos usar el operador resolución de ámbito para acceder al miembro de la clase de manera que el compilador entienda que nos estamos refiriendo a la definición del miembro de la clase. De otra manera estaríamos creando una variable global que no tiene ninguna relación con la clase. Ahora los constructores serán: CBox (double lv, double bv=1.0, double hv=1.0) { cout << endl << "Constructor called."; m_Length = lv; // Set values of m_Breadth = bv; // data members m_Height = hv; ObjectCount++; } CBox() // Default constructor { cout << endl << "Default constructor called."; m_Length = m_Breadth = m_Height = 1.0; ObjectCount++; }

56 Miembros datos estáticos
El programa de prueba es: int main(void) { CBox Boxes[5]; // Array of CBox objects declared CBox Cigar(8.0, 5.0 ,1.0); // Declare Cigar box cout << endl << endl << "Number of objects (through class) = " << CBox::ObjectCount; cout << endl << "Number of objects (through object) = " << Boxes[2].ObjectCount; cout << endl; return 0; }

57 Miembros datos estáticos
Obsérvese que la primera invocación al contador se hace a través de la clase mientras que la segunda a través del objeto. int main(void) { CBox Boxes[5]; // Array of CBox objects declared CBox Cigar(8.0, 5.0 ,1.0); // Declare Cigar box cout << endl << endl << "Number of objects (through class) = " << CBox::ObjectCount; cout << endl << "Number of objects (through object) = " << Boxes[2].ObjectCount; cout << endl; return 0; }

58 Miembros datos estáticos
Podemos visualizar la relación entre todas las partes en el siguiente diagrama: class CBox // Class definition at global scope { public: static int ObjectCount; // Count of objects in existence …………….. private: double m_Length; // Length of a box in inches double m_Breadth; // Breadth of a box in inches double m_Height; // Height of a box in inches }; double m_Length; double m_Breadth; double m_Height; …………. double m_Length; double m_Breadth; double m_Height; …………. double m_Length; double m_Breadth; double m_Height; …………. Objeto1 Objeto2 Objeto3 ObjectCount Una sola copia es compartida por todos los objetos de la clase

59 Funciones Miembro estáticas de una Clase
Funciones Miembros estáticas Cuando una función miembro de una clase se declara como estática se obtiene como resultado una función que es independiente de cualquiera de los objetos de dicha clase. El acceso a miembros de la clase desde la función debe hacerse usando el operador resolución de ámbito como lo haría cualquier función global para acceder a un miembro publico de la clase. Esta función tiene como ventaja que existe y puede ser llamada aun cuando no exista ningún objeto de la clase. En este ultimo caso solo miembros estáticos de la clase pueden ser accedidos dado que son los únicos que existen. De esta manera podemos llamar a una función estática para examine a un dato miembro estático y determine el numero de objetos en existencia. Una vez que un objeto de la clase existe podemos acceder a los miembros públicos y privados del mismo.

60 Funciones Miembro estáticas de una Clase
Funciones Miembros estáticas class CBox // Class definition at global scope { public: static int ObjectCount; // Count of objects in existence static int inspector(void ); // Prototipo …………. int CBox::inspector() // Definicion { cout << "(Inspector reported) :"; return(ObjectCount); } // Llamada cout << CBox::inspector(); cout << Boxes[2].inspector();

61 Funciones Miembro estáticas de una Clase
Funciones Miembros estáticas EX8_11M.CPP (Salida por consola) Number of objects (Inspector reported) :0 Antes de que exista algún objeto Default constructor called. Number of objects (Inspector reported) :5 Después de la creación del array Constructor called. Después de la creación de la caja de cigarros Number of objects (through class) = (Inspector reported) :6 Number of objects (through object) = (Inspector reported) :6

62 Punteros y Referencias a Objetos
No existe diferencias en cuanto al uso de punteros y referencias a objetos y las que se usan con variables de tipo nativo o del tipo struc. Ej: CBox Cigar(8.0, 5.0 ,1.0); // Creamos el objeto Cigar Punteros CBox * pBox; // Declaramos un puntero a Cbox pBox = &Cigar; // El puntero contiene la dirección del Objeto Cigar pBox  Volume(); // Muestra el volumen de la caja de cigarros Referencias CBox& rCigar=Cigar; //rCigar es una referencia a Cigar Cout << rCigar.Volume(); Muestra el volumen usando una referencia

63 Reserva dinámica de memoria
C++ provee de dos operadores new y delete para reservar memoria en forma dinámica. Sintaxis: Para tipos nativos: 1-Opcional puntero = new TIPO ( valor inicial1 ) ; Para liberar memoria (tipos nativos) delete puntero ; Para arrays: puntero = new TIPO [size] ; Para arrays: delete [ ] puntero;

64 Reserva dinámica de memoria
Ejemplos: int *p2int=0; p2int=new int; // Reservo un entero (malloc) if (p2int==0) // Me aseguro que haya lugar cout << "Error "; *p2int=12; // almaceno un dato en dicho lugar delete p2int; // Libero memoria (free)

65 Reserva dinámica de memoria
Reserva e inicialización int *p2int=0; p2int=new int (12); // Reservo un entero // e inicializo. if (p2int==0) // Me aseguro que haya lugar cout << "Error "; delete p2int; // Libero memoria (free)

66 Reserva dinámica de memoria
Arrays dinámicos p2int= new int[20]; // Creo espacio para un array de 20 enteros if(p2int==0) cout << "Error "; delete [ ] p2int; // Libero el array

67 Destructores Un destructor es una función que destruye el objeto cuando no se lo necesita mas o cuando sale del ámbito donde esta definido. En este ultimo caso se llama en forma automática. La destrucción del objeto significa liberar toda la memoria usada por sus miembros excepto los miembros estáticos de la clase (aun en ausencia de objetos). No recibe ni devuelve parámetros. El nombre de un destructor es el mismo de la clase (como los constructores) pero precedido por un tilde ~ . Por ejemplo para la clase CBox el destructor es : ~ CBox ( ) ;

68 Destructor por defecto
Todos los objetos creados en los ejemplos anteriores han sido destruidos en forma automática por el constructor por defecto. Esta función es generada por el compilador cuando no hay ningún destructor explicito. El destructor por defecto NO destruye a objetos o miembros del objeto que hayan sido creados dinámicamente (mediante el operador new). Veamos un ejemplo: class CBox // Class definition at global scope { public: // Destructor definition ~CBox() { cout << "Destructor called." << endl; }

69 Destructor por defecto
El main seria int main(void) { CBox Boxes[5]; // Array of CBox objects declared CBox Cigar(8.0, 5.0 ,1.0); // Declare Cigar box CBox Match(2.2, 1.1, 0.5); // Declare Match box CBox* pB1 = &Cigar; // Initialize pointer to Cigar object address CBox* pB2 = 0; // Pointer to CBox initialized to null cout << endl << "Volume of Cigar is " << pB1->Volume(); // Volume of obj. pointed to pB2 = Boxes; // Set to address of array Boxes[2] = Match; // Set 3rd element to Match cout << endl // Now access thru pointer << "Volume of Boxes[2] is " << (pB2 + 2)->Volume(); cout << endl; return 0;

70 Destructor por defecto
La salida del programa seria: Constructor called. Constructor called Volume of Cigar is 40 Volume of Boxes[2] is 1.21 Destructor called. Destructor called 7

71 Destructores y reserva dinámica de memoria
Cuando se crea un objeto hay situaciones en las que se necesita reservar memoria en forma dinámica para los miembros del objeto. El motivo fundamental es hacer un uso eficiente de la memoria, reservando así solamente la memoria necesaria. Supongamos que deseamos una clase capaz de albergar un mensaje en uno de sus miembros dato. Para realizar esto tenemos dos posibilidades una es crear un miembro dato como un array de caracteres lo suficiente grande como para almacenar todos los mensajes posibles mientras que la segunda consiste en reservar memoria con la longitud actual del mensaje en el momento de crear el objeto.

72 Destructores y reserva dinámica de memoria
De esas dos posibilidades la ultima es la mas apropiada si queremos un uso eficiente de la memoria del sistema. Para crear y destruir el array hacemos uso de los operadores new y delete Ej: private: char* pmessage; // Pointer to object text string // Constructor definition CMessage::CMessage(const char* text = "Default message") { pmessage = new char[strlen(text)+1]; // Allocate space for text strcpy(pmessage, text); // Copy text to new memory cout << endl << "Se llamo al constructor"; } // Destructor to free memory allocated by new CMessage::~CMessage() cout << "Destructor called." << endl; // Just to track what happens delete[] pmessage; // Free memory assigned to pointer

73 Destructores y reserva dinámica de memoria
La clase completa es: class CMessage { private: char* pmessage; // Pointer to object text string public: void ShowIt(void) // Function to display a message cout << endl << pmessage; } CMessage(const char*); // Constructor prototype ~CMessage(); // Destructor prototype };

74 Destructores y reserva dinámica de memoria
Ejemplo de uso de la clase CMessage: int main(void) { // Declare object CMessage Motto("A miss is as good as a mile."); // Dynamic object CMessage* pM = new CMessage("A cat can look at a queen."); Motto.ShowIt(); // Display 1st message pM->ShowIt(); // Display 2nd message cout << endl; delete pM; //Manually delete object created with new return 0; }

75 Destructores y reserva dinámica de memoria
Se llamo al constructor A miss is as good as a mile. A cat can look at a queen. Destructor called. CMessage Motto("A miss is as good as a mile."); CMessage* pM = new CMessage("A cat can look at a queen."); La creación del primer objeto sigue las mismas reglas que cuando usamos una variable automática en una función. El objeto es creado al entrar en la función (mediante el constructor de la clase) y eliminado en forma automática (a través del destructor de la clase) cuando termina la función.

76 Destructores y reserva dinámica de memoria
En el caso del segundo objeto la situación es diferente. En este caso el objeto fue creado en forma dinámica por nosotros : CMessage* pM = new CMessage("A cat can look at a queen."); Al ser creado se llama al constructor de la clase. Como consecuencia la destrucción del objeto corre por nuestra cuenta: delete pM; Nótese que como pM apunta a un objeto del tipo CMessage el operador delete llamara al destructor de la clase antes de liberar la memoria apuntada por pM. De esta manera el destructor tiene la oportunidad de liberar su propia memoria.

77 Implementación de constructores copiadores
Habíamos visto que cuando no suministrábamos ningún constructor copiador el compilador nos suministraba uno por defecto. Este ultimo realizaba la copia bit a bit de cada uno de sus miembros. Este mecanismo no funciona en todos los casos. Por ejemplo si creamos un objeto de la clase CMessage a partir de otro de la misma clase tendremos que el miembro pmessage de ambos objetos contendría la dirección del mismo mensaje !!! Este comportamiento no seria aceptable. pmessage “hello world” Copy CMessage Obj1 CMessage Obj2

78 Implementación de constructores copiadores
La copia correcta de los objetos debe dar el siguiente resultado: pmessage “hello world” Copy CMessage Obj1 CMessage Obj2 Es decir se debe duplicar TODO. Para que esto sea posible nosotros debemos suministrar nuestro propio copiador constructor.

79 Implementación de constructores copiadores
El constructor copiador debería ser una función capaz de duplicar (estrictamente) un objeto de una clase a partir de otro de la misma clase. El prototipo podría ser algo así: CBox(CBox abox) Cuando este constructor copiador es invocado: CBox mybox =Cigar ; Se generará una llamada a nuestro constructor copiador como sigue: CBox :: CBox(Cigar) ;

80 Implementación de constructores copiadores
No parece haber ningún problema en la definición del constructor copiador . Sin embargo como el argumento que recibe el constructor es por valor el constructor debe crear una copia local del argumento actual lo que lo lleva a tener que invocarse nuevamente (para poder hacer la copia) lo que termina en un numero infinito de llamadas. Evidentemente no se puede pasar el objeto por valor debe hacerse por referencia para así evitar las infinitas llamadas al constructor. Cuando se llama por referencia lo único que se copia es la dirección del objeto.

81 Implementación de constructores copiadores
Existe otra situación en la que también encontramos problemas: supongamos que tenemos las siguientes sentencias: CMessage MostrarMensaje (CMessage msg1) { cout << “El mensage recibido es:”; ShowIt (msg1); } CMessage saludo(“Hola”); void MostrarMensaje(saludo); Primero se crea el objeto saludo reservando memoria dinámica para almacenar el mensaje “Hola”. Luego se pasa por valor dicho objeto a la función MostrarMensaje() con lo cual se crea una copia del mismo mediante el CC por defecto. Una vez enviado el mensaje, termina la función y el objeto desaparece. Junto con el desaparece la zona de memoria reservada para almacenar el mensaje original (pues se invoca al destructor). Ahora el puntero al mensaje del objeto original apunta a una zona desconocida, y como consecuencia el objeto original nunca mas puede acceder a dicho mensaje. Por lo tanto una vez mas pasar valor no es lo que necesitamos sino por referencia.

82 Implementación de constructores copiadores
Entonces el prototipo del constructor copiador seria: CBox (const Cbox& initbox ) ; Al pasar por referencia NO se copia el objeto, solo es usado para inicializar la referencia al mismo. Lo que si se pasa es la dirección de manera que el constructor tiene acceso directo al objeto. De esta manera el constructor es invocado una vez. La expresión const en la declaración del argumento formal de la función garantiza que el objeto no va a ser modificado.

83 Implementación de constructores copiadores
La implementación del constructor copiador podría ser la siguiente CBox::CBox (const Cbox& initbox ) ; { m_Length = initbox. m_Length; m_Breadth = initbox. m_Breadth ; m_Height = initbox. m_Height ; } Nota: En el caso de la clase CBox este constructor copiador es redundante dado que el constructor copiador por defecto realiza exactamente lo mismo.

84 Implementación de constructores copiadores
Vimos que la clase Cmessage tenia problemas con el constructor copiador por defecto. Podemos crear entonces un constructor copiador especializado para dicha clase: CMessage(const Cmessage & initbox) { // Allocate space for text pmessage = new char[strlen(initbox.pmessage)+1]; strcpy(pmessage, initbox.pmessage); // Copy text to new memory } Ahora el nuevo objeto es totalmente independiente del primero. Recordar que cuando uno de los miembros de la clase se crea en forma dinámica HAY que proveer un constructor copiador para dicha clase. El por defecto NO funciona.

85 Sobrecarga de operadores
La sobrecarga de operadores nos habilita a usar los operadores como + ,-, *, etc. Para objetos definidos por el usuario. Existen restricciones sobre cuales operadores pueden ser sobrecargados: Los siguientes operadores NO pueden ser sobrecargados Operador resolución de entorno :: Operador condicional ?: Oprador selección de miembro . Operador Tamaño de Sizeof Operador puntero de de-referencia a miembro de la clase .*

86 Sobrecarga de operadores: Implementación
Si queremos implementar un operador para una clase dada debemos crear una función especial. Por ejemplo si queremos sobrecargar el operador Mayor que (>) , para la clase CBox la función se deberá declarar como: // Sobrecarga de operador “mayor que” int operator >(CBox& unacaja) ; // El nombre de la funcion es: operator > ( ) ;

87 Sobrecarga de operadores: Invocación
Con nuestra nueva función el operando izquierdo (operando implícito) deberá ser referenciado mediante el puntero THIS dentro de la función mientras que el operando derecho será el argumento actual de la función (operando explicito). (Recordar Box1.Volume(Box2)) int CBox::operator>(CBox& aBox) { return (this->Volume()) > (aBox.Volume()); } If (Box1 > Box2)

88 Sobrecarga de operadores: Ejemplo
class CBox // Class definition at global scope { public: // Constructor definition CBox(double lv=1.0, double bv=1.0, double hv=1.0) cout << endl << "Constructor called."; m_Length = lv; // Set values of m_Breadth = bv; // data members m_Height = hv; } // Function to calculate the volume of a box double Volume() return m_Length*m_Breadth*m_Height; int operator>(CBox& aBox); // Overloaded 'greater than' // Destructor definition ~CBox() { cout << "Destructor called." << endl; } private: double m_Length; // Length of a box in inches double m_Breadth; // Breadth of a box in inches double m_Height; // Height of a box in inches };

89 Sobrecarga de operadores: Ejemplo
int main(void) // Programa de prueba { CBox SmallBox(4.0,2.0, 1.0); CBox MediumBox(10.0, 4.0, 2.0); CBox BigBox(30.0, 20.0, 40.0); if(MediumBox > SmallBox) cout << endl << "MediumBox is Bigger than SmallBox"; if(MediumBox > BigBox) << "MediumBox is Bigger than BigBox"; else << "MediumBox is not Bigger than BigBox"; cout << endl; return 0; }

90 Sobrecarga de operadores:
Salida: Constructor called. MediumBox is Bigger than SmallBox MediumBox is not Bigger than BigBox Destructor called.

91 Sobrecarga de operadores
Existen todavía algunas limitaciones con la función operator>() por ejemplo situaciones como: if(unacaja > 20.0) no se pueden resolver dado que no podemos pasar como argumento una constante a una referencia que no lo es. La solución es apelar a las funciones sobrecargadas nuevamente así que podemos definir otra función operador que si contemple esta situación como: operator>(const double& value)

92 Sobrecarga de operadores
Esta función se implementaría como: int operator>(const double& value) { return (this->Volume()) > value; } Quedarían entonces por resolver situaciones como: if(20.0 > unacaja) Se podría argumentar que este tipo de situaciones se podrían resolver con la función operador: operator<( ) pero esto restringiría el uso del operador “ > ” para nuestra clase. Debe tenerse en cuenta que el uso de operadores debe ser lo mas natural posible. La cuestion es como?.

93 Sobrecarga de operadores
Una función miembro operador siempre provee al puntero “this” como medio para tener acceso al argumento a la izquierda de la función operador. Dado que el argumento a la izquierda es del tipo double no podemos implementar esta función como miembro de la clase. Esto nos deja con solo dos posibilidades o crear una función amiga o una función ordinaria. Usar una función amiga no tiene sentido en este caso pues no necesitamos acceder a los miembros privados de la clase. Por lo tanto una función ordinaria es la elección mas apropiada y seria: int operator > (const double& value, CBox& unacaja); Definida fuera de la definición de la clase y su implementación seria: int operator>(const double& value, CBox& aBox) // Ej: if (10.0 > SmallBox) { return value > aBox.Volume(); }

94 Sobrecarga de operadores
Una función ordinaria ( y de paso una función amiga) acceden la función Volume() usando el operador de selección de miembro pues esta ultima es publica. Pero que pasa si la clase carece de la función volume() como publica?. Es ese caso necesitaríamos una función amiga para acceder a los miembros privados de la clase. Otra solución es proveer de un conjunto de funciones miembro que devuelvan miembros privados de la clase y usar estas ultimas en una función ordinaria que implemente la operación de comparación. Veamos entonces la definición de la clase completa.

95 Sobrecarga de operadores
class CBox // Class definition at global scope { public: // Constructor definition CBox(double lv=1.0, double bv=1.0, double hv=1.0): m_Length(lv), m_Breadth(bv), m_Height(hv) cout << endl << "Constructor called."; } // Function to calculate the volume of a box double Volume() return m_Length*m_Breadth*m_Height; // Operator function for 'greater than' which compares volumes of CBox objects. int operator>(CBox& aBox) // Ej: if (MediumBox > SmallBox) return (this->Volume()) > (aBox.Volume()); // Function to compare a CBox object with a constant int operator>(const double& value) // Ej: if (MediumBox > 50.0) return (this->Volume()) > value; // ( Sige en la proxima hoja )

96 Sobrecarga de operadores
// Destructor definition ~CBox() { cout << "Destructor called." << endl;} private: double m_Length; // Length of a box in inches double m_Breadth; // Breadth of a box in inches double m_Height; // Height of a box in inches }; int operator>(const double& value, CBox& aBox); // Function prototype Ej: if (10.0 > SmallBox) int main(void) { CBox SmallBox(4.0,2.0, 1.0); CBox MediumBox(10.0, 4.0, 2.0); if(MediumBox > SmallBox) cout << endl << "MediumBox is Bigger than SmallBox"; // ( Sige en la proxima hoja )

97 Sobrecarga de operadores
if(MediumBox > 50.0) cout << endl << "MediumBox capacity is more than 50"; else << "MediumBox capacity is not more than 50"; if(10.0 > SmallBox) << "SmallBox capacity is less than 10"; << "SmallBox capacity is not less than 10"; cout << endl; return 0; } // Function comparing a constant with a CBox object int operator>(const double& value, CBox& aBox) // Ej: if (10.0 > SmallBox) { return value > aBox.Volume();

98 Sobrecarga de operadores
Obsérvese que la declaración de la función o prototipo debe ir después de la definición de la clase. Esto debe ser así porque la función hace referencia al nombre de clase en la lista de parámetros formales de la misma. Si se pone antes no funciona. Una forma de poder poner la declaración de la función al principio de todo es anteponer una declaración incompleta de la clase antes del prototipo de la función: class CBox; // declaracion incompleta de la clase int operator>(const double& value, CBox& aBox); // Function prototype De esta manera el compilador reconoce a CBox como un tipo definido por el usuario y cuya definición vendrá mas adelante, permitiendo así al compilador procesar al prototipo de la función.

99 Sobrecarga de operadores
Esta declaración adelantada o incompleta permite resolver situaciones en las que por ejemplo, se tienen dos clases en la que cada una tiene como miembro un puntero a un objeto de la otra clase. La salida del programa previo será: Constructor called. MediumBox is Bigger than SmallBox MediumBox capacity is more than 50 SmallBox capacity is less than 10 Destructor called.

100 Sobrecarga del operador Asignación (=)
Si no se provee del operador de asignación el compilador provee uno por defecto cuyo comportamiento es similar (pero no igual) al del copiador constructor que realiza la copia miembro a miembro . La diferencia esta en que en el caso del copiador constructor por defecto es llamado cuando : 1- Se declara un objeto de una clase y se usa un objeto de la misma para inicializarlo. 2- Se pasa un objeto por valor a una función (se crea una copia en el stack). En el caso del operador de asignación por defecto la invocación del mismo ocurre cuando el objeto a la izquierda y de la derecha ya son objetos pertenecientes a la misma clase. En otras palabras la diferencia esta que en el primer caso creamos al objeto a partir de uno existente, mientras que en el segundo los objetos ya existen.

101 Sobrecarga del operador Asignación (=)
El operador asignación por defecto funciona bien en tanto no se haya reservado memoria en forma dinámica para uno de sus miembros. De ser así expresiones como: Objeto2 = Objeto1; fallan pues como en el caso de la clase CMessage el puntero pmessage de ambos objetos termina apuntando a mismo string y por lo tanto cualquier cambio producido en uno de los objetos quedara reflejado sobre el otro. De la misma forma si uno de los dos objetos es destruido el puntero pmessage del otro quedara apuntando a una zona invalida. Lo que necesitamos es que el objeto destino cree su propia área de memoria para guardar el string. Una vez creada esta ultima se realiza la copia del string almacenado en el objeto original a esta nueva área. Dado la longitud del string en el objeto origen es diferente a la del destino lo primero que debemos hacer es liberar la zona usada por el objeto destino y reservar una zona nueva que se ajuste a la longitud del string del objeto original.

102 Sobrecarga del operador Asignación (=)
Pmessage (Origen) H O L A \0 Pmessage (Destino) H E L O \0 Objetos origen y destino antes del proceso de Asignación

103 Sobrecarga del operador Asignación (=)
Pmessage (Origen) H O L A \0 Pmessage (Destino) H E L O \0 Libero la memoria usada por el string del objeto destino (delete).

104 Sobrecarga del operador Asignación (=)
Pmessage (Origen) H O L A \0 ? Pmessage (Destino) Reservo memoria en forma dinámica para almacenar el un string de igual longitud que el del objeto origen (New).

105 Sobrecarga del operador Asignación (=)
Pmessage (Origen) H O L A \0 H O L A \0 Pmessage (Destino) Copio el string desde el objeto origen al destino (strcpy).

106 Sobrecarga del operador Asignación (=)
El código de la función que sobrecarga al operador asignación será : // Overloaded assignment operator for CMessage objects CMessage& operator=(const CMessage& aMess) { // Release memory for 1st operand delete[] pmessage; pmessage = new char[ strlen(aMess.pmessage) +1]; // Copy 2nd operand string to 1st strcpy(this->pmessage, aMess.pmessage); // Return a reference to 1st operand return *this; }

107 Sobrecarga del operador Asignación (=)
Existen algunos detalles que deben ser mencionados. Recordemos que una función que devolvía una referencia nos permitía escribir una expresión en la cual la función aparecía a la izquierda del operador ‘=‘. Ej: Tipo1 var=0: Tipo1& fun( ): fun( ) = var; Gracias a esta propiedad de las referencias podemos escribir lo siguiente: O sea: Objeto1= Objeto2= Objeto3; (Objeto1.operator=(Objeto2))= Objeto3; Y por lo tanto: (Objeto1. operator= (Objeto2)). operator=(Objeto3);

108 Sobrecarga del operador Asignación (=)
Existe todavía un problema potencial que pasa si ponemos: Objeto1= Objeto1 Si bien nadie haría algo así situaciones como estas pueden ocurrir fácilmente cuando uno de los objetos esta oculto tras un puntero. Objeto1= *p2objeto1; Donde p2objeto1 apunta a Objeto1. Bajo estas condiciones se liberaría la memoria del objeto1 para luego volver a reservar memoria basada en la longitud del string recién eliminado y posiblemente ya corrupto. En consecuencia la copia posterior daría resultados impredecibles

109 Sobrecarga del operador Asignación (=)
Este problema pede ser resuelto fácilmente si verificamos al principio de la función operador = que el objeto pasado como argumento no sea el mismo que el que invoca a la función: // Overloaded assignment operator for CMessage objects CMessage& operator=(const CMessage& aMess) { if(this == &aMess) // Check addresses, if equal return *this; // return the 1st operand // Release memory for 1st operand delete[] pmessage; pmessage = new char[ strlen(aMess.pmessage) +1]; // Copy 2nd operand string to 1st strcpy(this->pmessage, aMess.pmessage); // Return a reference to 1st operand return *this; }

110 Sobrecarga del operador Asignación (=)
El programa completo puede verse en Ex9_05.cpp. La definición de la clase es: // Definicion de la clase class CMessage { private: char* pmessage; // Pointer to object text string public: // Function to display a message void ShowIt(void) cout << endl << pmessage; } //Function to reset a message to * void Reset(void) char* temp=pmessage; while(*temp) *(temp++)='*'; //Continuacion // Overloaded assignment operator for CMessage objects CMessage& operator=(const CMessage& aMess) { if(this == &aMess) // Check addresses, if equal return *this; // return the 1st operand // Release memory for 1st operand delete[] pmessage; pmessage = new char[ strlen(aMess.pmessage) +1]; // Copy 2nd operand string to 1st strcpy(this->pmessage, aMess.pmessage); // Return a reference to 1st operand return *this; }

111 Sobrecarga del operador Asignación (=)
//Continuacion // Constructor definition CMessage(const char* text = "Default message") { // Allocate space for text pmessage = new char[ strlen(text)+1 ]; // Copy text to new memory strcpy(pmessage, text); } // Destructor to free memory allocated by new ~CMessage() // Just to track what happens cout << "Destructor called." << endl; // Free memory assigned to pointer delete[] pmessage; } }; // Fin de la clase

112 Sobrecarga del operador Asignación (=)
int main(void) { CMessage Motto1("The devil takes care of his own"); CMessage Motto2; cout << "Motto2 contains - "; Motto2.ShowIt(); cout << endl; Motto2 = Motto1; // Use new assignment operator Motto1.Reset(); // Setting Motto1 to ‘*’ doesn't // affect Motto2 cout << "Motto1 now contains - "; Motto1.ShowIt(); cout << "Motto2 still contains - "; return 0; } Motto2 contains - Default message The devil takes care of his own Motto1 now contains - ******************************* Motto2 still contains - Destructor called. Siguen siendo independientes

113 Sobrecarga del operador Adición (+)
La sobrecarga del operador + resulta probablemente unos de los aspectos mas interesantes del lenguaje pues depende de la aplicación como se define la operación suma. Por ejemplo en el caso de la clase Cbox esto puede ser así: El resultado de la suma es otra caja cuya altura es igual a la suma de las alturas de cada caja. El ancho es igual al ancho de la mayor y el largo es igual al de la mayor.

114 Sobrecarga del operador Adición (+)
Podemos definir al prototipo de la función operador suma ( operator+ ( ) ) como: // Function to add two CBox objects CBox operator+(const CBox& aBox) { // New object has larger length & breadth, and sum of heights return CBox(m_Length>aBox.m_Length? m_Length:aBox.m_Length, m_Breadth>aBox.m_Breadth? m_Breadth:aBox.m_Breadth, m_Height + aBox.m_Height); } El argumento se declaro como una referencia constante para evitar la copia innecesaria del mismo.

115 Sobrecarga del operador Adición (+)
Ejemplo: Definición de la clase class CBox // Class definition at global scope { public: // Constructor definition CBox(double lv=1.0, double bv=1.0, double hv=1.0): m_Height(hv) m_Length = lv > bv ? lv : bv; // Ensure that m_Breadth = bv < lv ? bv : lv; // length >= breadth } // Function to calculate the volume of a box double Volume() return m_Length*m_Breadth*m_Height; // Operator function for 'greater than' which // compares volumes of CBox objects. int CBox::operator>(CBox& aBox) return (this->Volume()) > (aBox.Volume()); // Continua en la proxima hoja

116 Sobrecarga del operador Adición (+)
// Function to compare a CBox object with a constant int operator>(const double& value) { return Volume() > value; } // Function to add two CBox objects CBox operator+(const CBox& aBox) // New object has larger length & breadth, and sum of heights return CBox(m_Length>aBox.m_Length? m_Length:aBox.m_Length, m_Breadth>aBox.m_Breadth? m_Breadth:aBox.m_Breadth, m_Height + aBox.m_Height); // Function to show the dimensions of a box void ShowBox(void) cout << m_Length << " " << m_Breadth << " " << m_Height << endl; private: double m_Length; // Length of a box in inches double m_Breadth; // Breadth of a box in inches double m_Height; // Height of a box in inches };

117 Sobrecarga del operador Adición (+)
int operator>(const double& value, CBox& aBox); // Function prototype int main(void) { CBox SmallBox(4.0, 2.0, 1.0); CBox MediumBox(10.0, 4.0, 2.0); CBox aBox; CBox bBox; aBox = SmallBox+MediumBox; cout << "aBox dimensions are "; aBox.ShowBox(); bBox = aBox+SmallBox+MediumBox; cout << "bBox dimensions are "; bBox.ShowBox(); return 0; } // Function comparing a constant with a CBox object int operator>(const double& value, CBox& aBox) return value > aBox.Volume(); Recordemos que la función int operator>(const double& value, CBox& aBox) no puede ser implementada como función miembro pues el operando izquierdo no es un objeto de la clase

118 Sobrecarga del operador Adición (+)
Recordemos que la función int operator>(const double& value, CBox& aBox) no puede ser implementada como función miembro pues el operando izquierdo no es un objeto de la clase. La funcion sobrecarga del operador + tambien se podria implementado mediante una funcion amiga: // Function to add two CBox objects friend CBox operator+(const CBox& aBox , const CBox bBox) El resultado es exactamente el mismo solo que debe usarse el operador de selección directa para tener acceso los miembros de los argumentos de la función

119 Templates para Clases Se pueden crear templates para clases de la misma manera que lo hacíamos para funciones: template <class Tipo1 > class nombre { definiciones (miembros, metodos ,etc) }; La palabra template le indica al compilador que se esta por definir un molde para la clase. Esta declaración se puede pensar como una función que recibe un parámetro de tipo class (que no tiene que ser necesariamente una clase).Este parámetro va a ser reemplazado por un tipo real (como int o Cbox) en el momento que el molde sea usado. 1 Se pueden usar otros nombres como T o T1.

120 Templates para Clases Los templates se comportan como un molde para tipos. La diferencia con los templates para funciones es que en este ultimo caso el compilador decidía la función a usar en función del tipo de los argumentos .Acá es el usuario del molde quien inserta el tipo que desea class CSamples { int m_value; } Tint template <class T> class CSamples { T m_value; } Tfloat class CSamples { float m_value; } TCBox class CSamples { CBox m_value; }

121 Templates para Clases Ejemplo: template <class T> class CSamples
{ private: T m_Values[100]; // Array to store samples int m_Free; // Index of free location in m_Values public: // Constructor definition to accept an array of samples CSamples(T values[], int count) m_Free = count<100 ? count:100; // Don't exceed the array for( int i=0 ; i<m_Free ; i++ ) m_Values[i] = values[i]; // Store count number of samples } // Constructor to accept a single sample CSamples(T value) m_Values[0] = value; // Store the sample m_Free = 1; // Next is free // Default constructor CSamples() m_Free = 0; // Nothing stored, so first is free // continua en la siguiente pagina Ejemplo:

122 Templates para Clases Ejemplo: // Viene de la pagina previa
// Function to add a sample int Add(T& value) { int OK = m_Free<100; // Indicates there is a free place if(OK) m_Values[m_Free++] = value; // OK true, so store the value return OK; } // Function to obtain maximum sample T Max() T theMax = m_Values[0]; // Set first sample as maximum for(int i=1 ; i<m_Free ; i++) // Check all the samples if(m_Values[i]>theMax) theMax = m_Values[i]; // Store any larger sample return theMax; };

123 Templates para Clases En el ejemplo anterior todas las funciones miembro fueron definidas dentro de la clase. Si se desea poner la definición fuera de la misma debemos definir un template para dicha función miembro separada del resto: template <class T> T CSamples <T> ::Max( ); { T theMax = m_Values[0]; // Set first sample as maximum for(int i=1 ; i<m_Free ; i++) // Check all the samples if(m_Values[i]>theMax) theMax = m_Values[i]; // Store any larger sample return theMax; } Obsérvese el parámetro <T> después del nombre de la clase. Esto permite al compilador identificar el nombre de la clase a la que la función miembro pertenece

124 Creación de objetos a partir del Template
Para crear un objeto de la clase a partir del template de la clase CSamples cuyas muestras sean del tipo float la declaración es la siguiente: CSamples <double> MyFloat (10.0); Esto define un objeto de la clase CSamples que es capaz de almacenar doubles. El objeto creado tiene una muestra cuyo valor es 10.0.

125 Creación de objetos a partir del Template
Podemos ahora crear un programa que cree un objeto capaz de albergar muestras tipo CBox int main(void) { CBox Boxes[] = { // Create an array of boxes CBox(8.0, 5.0, 2.0), // Initialize the boxes... CBox(5.0, 4.0, 6.0), CBox(4.0, 3.0, 3.0) }; // Create the CSamples object to hold CBox objects CSamples <CBox> MyBoxes(Boxes, sizeof Boxes/sizeof CBox); CBox MaxBox = MyBoxes.Max(); // Get the biggest box cout << endl // and output its volume << "The biggest box has a volume of " << MaxBox.Volume() << endl; return 0; }


Descargar ppt "Algoritmos y Estructuras de Datos"

Presentaciones similares


Anuncios Google