SlideShare una empresa de Scribd logo
1 de 51
Descargar para leer sin conexión
Programación Avanzada - Tema 4: Programación Genérica

             http://aulavirtual.uji.es

                José Luis Llopis Borrás

               22 de septiembre de 2010
Índice

1. Introducción                                                                           4

2. Funciones genéricas                                                                    5
   2.1. Definición e instanciación    . . . . . . . . . . . . . . . . . . . . . . . . .    7
   2.2. Restricciones de uso . . . . . . . . . . . . . . . . . . . . . . . . . . . .     11
   2.3. Especialización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .    13
   2.4. Sobrecarga . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     15

3. Clases genéricas                                                                      16
   3.1. Definición de métodos . . . . . . . . . . . . . . . . . . . . . . . . . . .       18
   3.2. Organización del código fuente     . . . . . . . . . . . . . . . . . . . . . .   21
   3.3. Parámetros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     26
   3.4. Herencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     29

4. Introducción a la STL                                                                 32
   4.1. Contenedores secuenciales . . . . . . . . . . . . . . . . . . . . . . . .        33
4.1.1. Instanciación . . . . . . . . . . . . . . . . . . . . . . . . . . . .    35
     4.1.2. Operaciones comunes . . . . . . . . . . . . . . . . . . . . . . .        37
4.2. Algoritmos genéricos . . . . . . . . . . . . . . . . . . . . . . . . . . . .    39
     4.2.1. Aritmética de punteros . . . . . . . . . . . . . . . . . . . . . . .     40
     4.2.2. Iteradores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .   42
     4.2.3. Algoritmos genéricos en la biblioteca STL . . . . . . . . . . . . .      47
     4.2.4. Ejemplos de utilización de algoritmos genéricos . . . . . . . . . .      48
1 Introducción
  ® Ciertos algoritmos son independientes del tipo de datos sobre los que operan.
        µ Ejemplo: el cálculo del menor de dos valores enteros es idéntico al cálculo del
             menor (alfabéticamente) de dos strings:
             int min(int a, int b) { return a<b ? a : b; }
             string min(string a, string b) { return a<b ? a : b; }
        µ Implementaremos estos algoritmos con funciones genéricas.

  ® Ciertos tipos de datos pueden definirse de forma independiente (de al menos
        alguno) de los tipos de datos que manipulan.
        µ Ejemplo: la implementación de una lista de enteros sólo difiere de la de una
             lista de strings en el tipo de información que almacena.
        µ Implementaremos estos tipos de datos con clases genéricas.

  ® La genericidad es un potente mecanismo que permite la reutilización de código.
Programación Avanzada - Tema 4: Programación Genérica – 4
2 Funciones genéricas
  ® Supongamos que debemos implementar una función que imprima en la salida
        estándar los valores de un array de enteros:
        void i m p r i m i r ( i n t v [ ] , i n t t a l l a ) {
            c o u t < < "[ " ;
            f o r ( i n t i = 0 ; i < t a l l a ; i ++)
               cout < < v [ i ] < < " " ;
            c o u t < < "]" < < e n d l ;
        }


        i n t main ( ) {
            i n t v1 [ 5 ] = { 5 , 2 , 3 , 4 , 1 } ;
            i m p r i m i r ( v1 , 5 )
        }


        [ 5 2 3 4 1 ]




Programación Avanzada - Tema 4: Programación Genérica – 5
2 Funciones genéricas (II)
  ® ...y más tarde necesitamos implementar otra función que imprima los valores de un
        array de números reales:
        void i m p r i m i r ( f l o a t v [ ] , i n t t a l l a ) {
            c o u t < < "[ " ;
            f o r ( i n t i = 0 ; i < t a l l a ; i ++)
               cout < < v [ i ] < < " " ;
            c o u t < < "]" < < e n d l ;
        }


        i n t main ( ) {
            f l o a t v2 [ 3 ] = { 3 . 3 , 1 , 0 . 9 9 } ;
            i m p r i m i r ( v2 , 3 )
        }


        [ 3.3 1 0.99 ]



  ® Las dos versiones sólo se diferencian en el tipo base del array.
Programación Avanzada - Tema 4: Programación Genérica – 6
2.1 Definición e instanciación

  ® Implementamos una función genérica como única versión:
        t e m p l a t e < class T > void i m p r i m i r ( T v [ ] , i n t t a l l a ) {
            c o u t < < "[ " ;
            f o r ( i n t i = 0 ; i < t a l l a ; i ++)
               cout < < v [ i ] < < " " ;
            c o u t < < "]" < < e n d l ;
        }


        i n t main ( ) {
            i n t v1 [ 5 ] = { 5 , 2 , 3 , 4 , 1 } ;
            f l o a t v2 [ 3 ] = { 3 . 3 , 1 , 0 . 9 9 } ;
            i m p r i m i r ( v1 , 5 ) ;
            i m p r i m i r ( v2 , 3 ) ;
        }


        [ 5 2 3 4 1 ]
        [ 3.3 1 0.99 ]


Programación Avanzada - Tema 4: Programación Genérica – 7
2.1 Definición e instanciación (II)

  ® El compilador utiliza el código de la función genérica como plantilla para crear
        automáticamente dos funciones sustituyendo T por un tipo concreto:
        µ Con T=int se crea la función void imprimir(int v[], int talla) para satisfacer la
             llamada a imprimir(v1, 5).
        µ Con T=float se crea la función void imprimir(float v[], int talla) para satisfacer
             la llamada a imprimir(v2, 3).

  ® Esta operación recibe el nombre de instanciación.




Programación Avanzada - Tema 4: Programación Genérica – 8
2.1 Definición e instanciación (III)

  ® Vamos a añadir una función genérica
                                                            i n t main ( ) {
        que permita la ordenación de arrays:
                                                                i n t v1 [ 5 ] = { 5 , 2 , 3 , 4 , 1 } ;
                                                                ordenar ( v1 , 5 ) ;           / / T= i n t
 t e m p l a t e < class T>                                     i m p r i m i r ( v1 , 5 ) ; / / T= i n t
 void i n t e r c a m b i a r ( T & ip1 , T & i p 2 ) {
     T aux ;                                                    f l o a t v2 [ 3 ] = { 3 . 3 , 1 . 0 , 0 . 9 9 } ;
     aux = i p 1 ;                                              ordenar ( v2 , 3 ) ;           / / T= f l o a t
     ip1 = ip2 ;                                                i m p r i m i r ( v2 , 3 ) ; / / T= f l o a t
     i p 2 = aux ;
 }                                                              char v3 [ 5 ] = { ’e’ , ’o’ , ’a’ , ’u’ , ’i’ } ;
 / / ordenación ascendente método b u r b u j a                 ordenar ( v3 , 5 ) ;           / / T=char
 t e m p l a t e < class T>                                     i m p r i m i r ( v3 , 5 ) ; / / T=char
 void ordenar ( T v [ ] , i n t t a l l a ) {               }
     f o r ( i n t i = t a l l a − 1; i > = 1 ; i −−)
        f o r ( i n t j = 0 ; j < i ; j ++)
                                                            [ 1 2 3 4 5 ]
           i f ( v [ j ] > v [ j +1])
                                                            [ 0.99 1 3.3 ]
               intercambiar ( v [ j ] , v [ j +1]) ;
                                                            [ a e i o u ]
 }


Programación Avanzada - Tema 4: Programación Genérica – 9
2.1 Definición e instanciación (IV)

  ® En la implementación de la función genérica imprimir() la única operación que
        realizamos sobre valores de tipo T es la de salida de datos: cout << v[i]
        µ El compilador necesita de esa operación para crear instancias de la función.
        µ Esto impone una restricción sobre las posibles instancias: sólo se admitirán
             aquellos tipos T para los que la operación de salida << esté definida.

  ® ¿Qué restricciones impone las funciones genéricas ordenar() e intercambiar()?
        µ En la función ordenar() se necesita el operador > entre valores de tipo T.
        µ En la función intercambiar() se necesita que el operador = (asignación) esté
             correctamente definido entre valores de tipo T.
        µ En la función intercambiar() se necesita que un constructor sin argumentos
             que permita crear objetos de tipo T (constructor por omisión).



Programación Avanzada - Tema 4: Programación Genérica – 10
2.2 Restricciones de uso

  ® Teniendo en cuenta las restricciones mencionadas antes, ¿podemos instanciar
        imprimir() y ordenar() para arrays de objetos de la clase Cadena (tal y como la
        definimos en el Tema 2)?
        Cadena v4 [ 3 ] = { "tigre" , "gato" , "leon" } ;
        ordenar ( v4 , 3 ) ;
        i m p r i m i r ( v4 , 3 ) ;



  ® Para la función imprimir() no hay problema puesto que existe la función:
        ostream & operator < < ( ostream & canal , const Cadena & c ) ;



  ® Para la función intercambiar() tampoco, puesto que la asignación entre cadenas
        está definida:
        Cadena & Cadena : : operator = ( const Cadena & c ) ;



Programación Avanzada - Tema 4: Programación Genérica – 11
2.2 Restricciones de uso (II)

  ® Sin embargo, la función ordenar() necesita una función no definida en la clase
        Cadena:
        bool Cadena : : operator > ( const Cadena & c ) const ;



  ® El compilador nos informa del error:
        t e s t . cpp : I n f u n c t i o n ‘ void ordenar ( T ∗ , i n t ) [ w i t h T = Cadena ] ’:
        test.cpp:247:   instantiated from here
        test.cpp:218: no match for ‘Cadena& > Cadena&’ operator




Programación Avanzada - Tema 4: Programación Genérica – 12
2.3 Especialización

  ® Para solucionar el problema, definimos una nueva versión no genérica de
        ordenar() para arrays de objetos de tipo Cadena. Este proceso recibe el nombre
        de especialización.
        / / e s p e c i a l i z a c i ó n para a r r a y s de cadenas
        void ordenar ( Cadena v [ ] , i n t t a l l a ) {
            f o r ( i n t i = t a l l a − 1; i > = 1 ; i −−)
               f o r ( i n t j = 0 ; j < i ; j ++)
                   i f ( strcmp ( v [ j ] . s t r , v [ j + 1 ] . s t r ) >0)
                       intercambiar ( v [ j ] , v [ j +1]) ;
        }


        i n t main ( ) {
            Cadena v4 [ 3 ] = { "tigre" , "gato" , "leon" } ;
            ordenar ( v4 , 3 ) ;
            i m p r i m i r ( v4 , 3 ) ;
        }


        [ gato l e o n t i g r e ]

Programación Avanzada - Tema 4: Programación Genérica – 13
2.3 Especialización (II)

  ® Observa que en la función ordenar() accedemos a la parte privada de la clase
        Cadena en las expresiones v[j].str y v[j+1].str. Por tanto deberemos declarar la
        función ordenar() como friend de Cadena.

  ® En este ejemplo concreto, podemos optar por una segunda solución que no
        necesita especializar la función genérica ordenar(): podemos simplemente dotar a
        la clase Cadena del operador >.
        class Cadena {
             ...
        public :
             ...
             bool operator >( const Cadena & s ) const ;
        };


        bool Cadena : : operator >( const Cadena & s ) const {
             r e t u r n ( strcmp ( s t r , s . s t r ) >0) ? t r u e : f a l s e ;
        }


Programación Avanzada - Tema 4: Programación Genérica – 14
2.4 Sobrecarga

  ® Las funciones genéricas pueden ser
                                                             template < class T>
        sobrecargadas de la misma forma
                                                             T min ( T a , T b ) {
        que las funciones ordinarias.                            r e t u r n ( a<b ? a : b ) ;
                                                             }
        µ Podemos especificar más de un
             tipo genérico (segundo ejemplo
                                                             template < class T1 , class T2>
             de la derecha).                                 T1 min ( T1 a , T2 b ) {
                                                                 // ...
        µ Al menos alguno de los                             }
             parámetros de la función debe ser
             genérico, si no, el compilador no               template < class T > / / i n c o r r e c t o

             puede saber cómo instanciar el                  T min ( i n t a , i n t b ) {
                                                                 r e t u r n ( a<b ? a : b ) ;
             tipo T (tercer ejemplo de la                    }
             derecha).



Programación Avanzada - Tema 4: Programación Genérica – 15
3 Clases genéricas
 Una clase genérica es una plantilla de definición de una clase que puede ser
 instanciada en múltiples versiones concretas.

  ® Declaramos la clase genérica Vector que almacena valores de tipo T:
        t e m p l a t e < class T>
        class V e c t o r {
             T ∗ v;
             int t a l l a ;
        public :
             Vector ( i n t ) ;
             V e c t o r ( const Vector <T> & ) ;
             ~Vector ( ) ;
             Vector <T > & operator =( const Vector <T > & ) ;


             T & operator [ ] ( i n t ) ;
             void i m p r i m i r ( ) const ;
             i n t T a l l a ( ) const { r e t u r n t a l l a ; }
        };


Programación Avanzada - Tema 4: Programación Genérica – 16
3 Clases genéricas (II)
  ® Podemos instanciar la clase Vector para T=int o T=Cadena:
        i n t main ( ) {
            Vector < i n t > v I n t ( 3 ) ; / / I n s t a n c i a c i ó n : T= i n t
            f o r ( i n t i = 0 ; i < v I n t . T a l l a ( ) ; i ++)
               cin > > vInt [ i ] ;
            vInt . imprimir () ;


            Vector <Cadena > vCad ( 3 ) ; / / I n s t a n c i a c i ó n : T=Cadena
            f o r ( i n t i = 0 ; i <vCad . T a l l a ( ) ; i ++)
               c i n > > vCad [ i ] ;
            vCad . i m p r i m i r ( ) ;
        }


        3 4 5
        [ 3 4 5 ]
        h o l a que t a l
        [ h o l a que t a l ]



Programación Avanzada - Tema 4: Programación Genérica – 17
3.1 Definición de métodos

  ® Las funciones miembro de una clase genérica son, a su vez, funciones
        genéricas.

  ® Atención: la clase Vector no existe como tal, sólo es una plantilla. Hay que
        escribir, por tanto, Vector<T>.
 t e m p l a t e < class T > / / c o n s t r u c t o r
 Vector <T > : : V e c t o r ( i n t t ) {
     talla = t ;
     v=new T [ t a l l a ] ;
 }


 t e m p l a t e < class T > / / c o n s t r u c t o r c o p i a
 Vector <T > : : V e c t o r ( const Vector <T > & unVector ) {
     t a l l a = unVector . t a l l a ;
     v = new T [ t a l l a ] ;
     f o r ( i n t i = 0 ; i < t a l l a ; i ++)
        v [ i ] = unVector . v [ i ] ;
 }

Programación Avanzada - Tema 4: Programación Genérica – 18
3.1 Definición de métodos (II)

 t e m p l a t e < class T > / / d e s t r u c t o r
 Vector <T > : : ~ V e c t o r ( ) {
     delete [ ] v ;
 }


 t e m p l a t e < class T > / / operador a s i g n a c i ó n
 Vector <T > & Vector <T > : : operator =( const Vector <T > & unVector ) {
     i f ( t h i s ! = & unVector ) {
         i f ( t a l l a ! = unVector . t a l l a ) {
             delete [ ] v ;
             t a l l a = unVector . t a l l a ;
             v = new T [ t a l l a ] ;
         }
         f o r ( i n t i = 0 ; i < t a l l a ; i ++)
             v [ i ] = unVector . v [ i ] ;
     }
     return ∗ this ;
 }


Programación Avanzada - Tema 4: Programación Genérica – 19
3.1 Definición de métodos (III)

 t e m p l a t e < class T > / / operador s u b í n d i c e
 T & Vector <T > : : operator [ ] ( i n t i ) {
     return v [ i ] ;
 }


 t e m p l a t e < class T > / / f u n c i ó n i m p r i m i r ( )
 void Vector <T > : : i m p r i m i r ( ) const {
     c o u t < < "[ " ;
     f o r ( i n t i = 0 ; i < t a l l a ; i ++)
        cout < < v [ i ] < < " " ;
     c o u t < < "]" < < e n d l ;
 }




Programación Avanzada - Tema 4: Programación Genérica – 20
3.2 Organización del código fuente

 El modelo tradicional de organización de
 código fuente —modelo de compilación                                 Listado 1: MiClaseGenerica.h
 separada—, no funciona con módulos                          template <class T>
 que contienen clases genéricas.                             class MiClaseGenerica {
                                                                  private :
  ® Recuerda que este modelo                                        T dato ;
                                                                  public :
        consistente en separar la declaración
                                                                    MiClaseGenerica ( T ) ;
        y la implementación de un módulo                            void i m p r i m i r ( ) const ;
        (clase) en dos ficheros separados,                    };

        con extensiones .h y .cpp
        respectivamente.

  ® Veamos el ejemplo de la clase
        MiClaseGenerica a continuación.



Programación Avanzada - Tema 4: Programación Genérica – 21
3.2 Organización del código fuente (II)


          Listado 2: MiClaseGenerica.cpp                         Listado 3: TestMiClaseGenerica.cpp
 # include "MiClaseGenerica.h"                               # include "MiClaseGenerica.h"
 # include < iostream >
 using namespace s t d ;                                     i n t main ( ) {
                                                                 MiClaseGenerica < i n t >         a(123) ;
 template < class T>                                             MiClaseGenerica < f l o a t > b ( 3 . 1 4 1 6 ) ;
 MiClaseGenerica <T > : : MiClaseGenerica ( T x )                MiClaseGenerica <char >           c ( ’#’ ) ;
 : dato ( x )
 { }                                                             a . imprimir () ;
                                                                 b . imprimir () ;
 template < class T>                                             c . imprimir () ;
 void MiClaseGenerica <T > : : i m p r i m i r ( ) const {   }
     c o u t < < dato < < e n d l ;
 }




Programación Avanzada - Tema 4: Programación Genérica – 22
3.2 Organización del código fuente (III)

  ® Efectivamente, ¡la compilación falla!

                                         Listado 4: Resultado de la compilación
        $ g++ − c MiClaseGenerica . cpp
        $ g++ − c TestMiClaseGenerica . cpp
        $ g++ − o Test MiClaseGenerica . o TestMiClaseGenerica . o
        / u s r / b i n / l d : Undefined symbols :
        MiClaseGenerica <char > : : MiClaseGenerica ( char )
        MiClaseGenerica < f l o a t > : : MiClaseGenerica ( f l o a t )
        MiClaseGenerica < i n t > : : MiClaseGenerica ( i n t )
        MiClaseGenerica <char > : : i m p r i m i r ( ) const
        MiClaseGenerica < f l o a t > : : i m p r i m i r ( ) const
        MiClaseGenerica < i n t > : : i m p r i m i r ( ) const
        c o l l e c t 2 : ld returned 1 e x i t status




Programación Avanzada - Tema 4: Programación Genérica – 23
3.2 Organización del código fuente (IV)

  ® ¿Por qué falla el modelo de compilación separada?
        µ Un clase genérica (template) no es una clase, sino un patrón que el compilador
             usa para generar una o más clases.
        µ Para que el compilador pueda generar código, debe ver a la vez la
             implementación de la clase (no sólo la declaración) y los tipos específicos
             usados para instanciar el patrón.
           ª Si deseamos usar MiClaseGenerica<int>, el compilador necesita ver a la
                 vez la implementación de la clase MiClaseGenerica<T> y el lugar donde
                 instanciamos T como int.
           ª La implementación de MiClaseGenerica<T> está en un fichero y el lugar
                 donde instanciamos T en otro fichero. Ambos ficheros se compilan
                 separadamente y, por tanto, el compilador no sabe que tipo concreto es T
                 cuando compila la clase genérica, así que no genera ningún código.

Programación Avanzada - Tema 4: Programación Genérica – 24
3.2 Organización del código fuente (V)

  ® Existen 3 soluciones al problema:
        1. Mover físicamente la implementación de la clase al fichero .h, con lo que
             declaración e implementación estarán en el mismo lugar.
        2. (Recomendada) Dejar por separado declaración e implementación, pero incluir
             la implementación (fichero .cpp) donde se realiza la instanciación:

                                                    TestMiClaseGenerica.cpp
             # include "MiClaseGenerica.cpp"
             ...


        3. Utilizar la palabra reservada export. Hacer un compilador que la soporte es
             muy complicado. Sólo uno lo ha hecho: Comeau C++, pero no GCC.

                                                         MiClaseGenerica.h
             export template <class T > class MiClaseGenerica {
             ...


Programación Avanzada - Tema 4: Programación Genérica – 25
3.3 Parámetros

  ® Las clases genéricas pueden tomar dos tipos de parámetros:
        µ Tipos genéricos, como los identificadores T, T1, T2, TipoBase, etc.
        µ Tipos ordinarios, como los tipos int o char.

  ® En el siguient ejemplo definimos la clase VectorEstatico para almacener vectores
        cuya talla es conocida en tiempo de compilación.

                                              Clase VectorEstatico: declaración
        template < i n t tam , class T > / / Parámetros : t i p o o r d i n a r i o i n t y t i p o g e n é r i c o T
        class V e c t o r E s t a t i c o {
             T v [ tam ] ;
        public :
             T & operator [ ] ( i n t ) ;
             void i m p r i m i r ( ) const ;
        };




Programación Avanzada - Tema 4: Programación Genérica – 26
3.3 Parámetros (II)

  ® ...y aquí está la implementación:

                                          Clase VectorEstatico: implementación
        template < i n t tam , class T>
        void V e c t o r E s t a t i c o <tam , T > : : i m p r i m i r ( ) const {
            c o u t < < "[ " ;
            f o r ( i n t i = 0 ; i <tam ; i ++)
              cout < < v [ i ] < < " " ;
            c o u t < < "]" < < e n d l ;
        }


        template < i n t tam , class T>
        T & V e c t o r E s t a t i c o <tam , T > : : operator [ ] ( i n t i ) {
            return v [ i ] ;
        }




Programación Avanzada - Tema 4: Programación Genérica – 27
3.3 Parámetros (III)

  ® La instanciación de clases con parámetros constantes se realiza así:
        i n t main ( ) {
            V e c t o r E s t a t i c o <3 , i n t > v 3 I n t ;
            f o r ( i n t i = 0 ; i < 3 ; i ++)
               cin > > v3Int [ i ] ;
            v3Int . imprimir () ;


            V e c t o r E s t a t i c o <5 , i n t > v 5 I n t ;
            f o r ( i n t i = 0 ; i < 5 ; i ++)
               cin > > v5Int [ i ] ;
            v5Int . imprimir () ;
        }



  ® Atención: los tipos de las variables v3Int y v5Int son diferentes e incompatibles:
        µ v3Int es del tipo VectorEstatico de 3 componentes enteras.
        µ v5Int es del tipo VectorEstatico de 5 componentes enteras.

Programación Avanzada - Tema 4: Programación Genérica – 28
3.4 Herencia

 Podemos derivar clases genéricas de otras clases genéricas.

  ® Ejemplo: definimos la clase VectorSeguro como especialización de Vector. Ahora
        el operator[] comprobará si el índice está fuera de rango.

                                              Clase VectorSeguro: declaración
        template < class T>
        class VectorSeguro : public Vector <T > {
        public :
             VectorSeguro ( i n t i ) : Vector <T>( i ) { }
             T & operator [ ] ( i n t ) ;
        };



  ® Observa que usamos la lista de inicialización para pasar un argumento al
        constructor de la clase base.



Programación Avanzada - Tema 4: Programación Genérica – 29
3.4 Herencia (II)

  ® Observa que en la sentencia return llamamos a la función operator[ ] de la clase
        base.

                                          Clase VectorSeguro: implementación
        template < class T>
        T & VectorSeguro <T > : : operator [ ] ( i n t i ) {
            i f ( i < 0 | | i > t h i s −>T a l l a ( ) ) {
                c e r r < < "Error: Indice fuera de rango" < < e n d l ;
                exit (1) ;
            }
            r e t u r n Vector <T > : : operator [ ] ( i ) ;
        }




Programación Avanzada - Tema 4: Programación Genérica – 30
3.4 Herencia (III)

  ® Probamos la clase VectorSeguro:
        i n t main ( ) {
            VectorSeguro < i n t > v ( 3 ) ;


            f o r ( i n t i = 0 ; i <v . T a l l a ( ) ; i ++)
               cin > > v [ i ] ;
            v . imprimir () ;


            v [10]=33;
        }


        7 8 9
        [ 7 8 9 ]
        E r r o r : I n d i c e f u e r a de rango




Programación Avanzada - Tema 4: Programación Genérica – 31
4 Introducción a la STL
 La biblioteca estándar de plantillas (STL) contiene componentes genéricos de
 (básicamente) dos tipos:

  ® Contenedores
        µ Son clases genéricas que sirven para almacenar (contener) colecciones de
             elementos de algún tipo. Ejemplos: vector, list, set, map, etc.

  ® Algoritmos genéricos
        µ Son operaciones aplicables a los contenedores y los arrays.
        µ Son independientes del tipo de datos concreto sobre el que operan (int, char,
             string, . . . ). Por ello están implementadas usando funciones genéricas.
        µ Son independientes del tipo de contenedor en que los elementos están
             almacenados (array, list, vector, set, . . . ). Esta independencia se consigue
             gracias a que no operan directamente sobre ellos, sino a través de iteradores.

Programación Avanzada - Tema 4: Programación Genérica – 32
4.1 Contenedores secuenciales

 Un contenedor secuencial contiene una colección ordenada de elementos de un
 mismo tipo. Veamos dos de los más importantes:

  ® vector
        µ Mantiene sus elementos en un área contigua de memoria.
        µ El acceso aleatorio es eficiente: es igual de rápido acceder al elemento 5o que
             al 17o o al 9o .
        µ El operador de subíndice [ ] permite acceder a sus componentes.
        µ Sin embargo, la inserción de un elemento en cualquier posición que no sea la
             última es ineficiente ya que implica el desplazamiento de los elementos a la
             derecha del insertado.
        µ Por el mismo motivo, el borrado de un elemento distinto del último es también
             ineficiente.

Programación Avanzada - Tema 4: Programación Genérica – 33
4.1 Contenedores secuenciales (II)

  ® list
        µ Mantiene sus elementos en áreas de memoria no contigua (concretamente,
             nodos doblemente enlazados).
        µ La inserción y borrado de elementos en cualquier posición es eficiente.
        µ Sin embargo, el acceso aleatorio es menos eficiente: para acceder al elemento
             5o , 17o y 9o hay que visitar todos los intermedios.




Programación Avanzada - Tema 4: Programación Genérica – 34
4.1.1 Instanciación

  ® Para usar un contenedor secuencial, debemos incluir el fichero de cabecera
        asociado:
        #include <vector>
        #include <list>
  ® Disponemos de cinco modos de instanciar un objeto contenedor secuencial:
        1. Crear un contenedor vacío:
             vector<int> v1;
             list<Complejo> lista1;
        2. Crear un contenedor de algún tamaño no necesariamente constante. Cada
             elemento se inicializa con el valor por defecto (los int o double con cero):
             vector<int> v2 (100);
             list<Cadena> lista2 (talla);


Programación Avanzada - Tema 4: Programación Genérica – 35
4.1.1 Instanciación

        3. Crear un contenedor de un tamaño y valor inicial dado:
             vector<string> v3 (16, "una cadena" );
             list<float> lista3 (100, 3.1416 );
        4. Crear un contenedor como copia de otro:
             vector<string> v4 (v3);
             list<float> lista4 (lista3);
        5. Crear un contenedor usando un par de iteradores. (Más adelante volveremos a
             tratar este asunto).




Programación Avanzada - Tema 4: Programación Genérica – 36
4.1.2 Operaciones comunes

  ® Algunas operaciones comunes:
        µ ¿Qué talla tiene el contenedor? ¿Está vacío?: size() y empty()
        µ La asignación, el test de igualdad y desigualdad: =, == y !=
        µ Acceso al primer y último elemento: front() y back()
        µ Inserción y borrado del último: push_back() y pop_back()

  ® El contenedor list, pero no el vector, permite además:
        µ Inserción y borrado del primero: push_front() y pop_front()




Programación Avanzada - Tema 4: Programación Genérica – 37
4.1.2 Operaciones comunes (II)



                        Ejemplo de uso                                         Salida
 # include < iostream >                                      12
 # include < l i s t >                                       45
 using namespace s t d ;                                     33
                                                             111
 i n t main ( ) {                                            35
     l i s t <int > miLista ; int valor ;                    miLista = { 12 45 33 111 35 }
     while ( c i n > > v a l o r )
         m i L i s t a . push_back ( v a l o r ) ;
     c o u t < < "miLista = { " ;
     while ( ! m i L i s t a . empty ( ) ) {
         cout < < miLista . f r o n t ( ) < < ’ ’ ;
         miLista . pop_front ( ) ;
     }
     c o u t < < ’}’ < < e n d l ;
 }




Programación Avanzada - Tema 4: Programación Genérica – 38
4.2 Algoritmos genéricos

 Supongamos que necesitamos escribir una función que realice la búsqueda secuencial
 de la primera aparición de un valor determinado en un contenedor vector y devuelva
 un puntero a él o cero (NULL) si no se encuentra:
 template < class T > const T ∗ f i n d ( const v e c t o r <T > & v , const T & v a l o r ) {
     f o r ( i n t i = 0 ; i <v . s i z e ( ) ; i ++)
        i f ( v [ i ]== v a l o r )
           return & v [ i ] ;
     r e t u r n NULL ;
 }


  ® ¿Es posible modificar la función para que también funcione con arrays? ¿Y con
        otros contenedores secuenciales?

  ® Si conseguimos estos dos objetivos habremos implementado un algoritmo
        genérico de búsqueda secuencial aplicable a cualquier contenedor secuencial
        incluyendo los arrays. El problema radica en encontrar un tratamiento común
        para arrays y contenedores.
Programación Avanzada - Tema 4: Programación Genérica – 39
4.2.1 Aritmética de punteros

  ® Planteamos una versión de find() para arrays:
        template < class T>
        T ∗ f i n d ( T ∗ v , i n t tam , const T & v a l o r ) {
            i f ( ! v | | tam <1) r e t u r n 0 ;
            f o r ( i n t i = 0 ; i <tam ; + + i , + + v )
               i f ( ∗ v = = v a l o r ) return v ;
            r e t u r n 0 ; / / NULL
        }
                                                                    v               …

  ® Esta versión realiza bien su cometido pero hemos                    0   1   2   … tam-1

        tenido que introducir un nuevo argumento, tam (la talla
        del array ), puesto que esa información es necesaria.

  ® Debemos evitar dicho parámetro ya que los
        contenedores como vector o list ya contienen
        internamente su talla.

Programación Avanzada - Tema 4: Programación Genérica – 40
4.2.1 Aritmética de punteros (II)

  ® Planteamos una segunda versión de find() para arrays
        en la que sustituimos el tamaño del array por un
        puntero que sirve como centinela que marca el final:
        template < class T>
                                                                                first                    last
        T ∗ f i n d ( T ∗ f i r s t , T ∗ l a s t , const T & v a l o r ) {
            if ( !     first     || !    l a s t ) return 0 ;
            while ( f i r s t ! = l a s t )                                       1     3   5   7   11

               i f ( ∗ f i r s t = = v a l o r ) return f i r s t ;
              else ++ f i r s t ;                                               array
            r e t u r n 0 ; / / NULL                                          array+5
        }
                                                                                last y array+5 apuntan al
                                                                                  elemento siguiente del
                                                                                 último, aunque no exista
        i n t main ( ) {
            int array [5]={1 ,3 ,5 ,7 ,11};
            i f ( f i n d ( array , array +5 , 7) )
            ...



Programación Avanzada - Tema 4: Programación Genérica – 41
4.2.2 Iteradores

  ® Gracias a la aritmética de punteros hemos encontrado una buena estrategia para
        pasar los argumentos a find(): utilizamos la dirección del primer elemento del array
        y la del elemento siguiente al último.

  ® Para aplicar la misma estrategia a contenedores secuenciales, deberemos usar
        una abstracción del concepto de puntero: el iterador:
        µ Cada contenedor define el tipo: contenedor<tipobase>::iterator
        µ Cada contenedor define las funciones begin() y end() que devuelven iteradores
             que “apuntan” al primero y al siguiente del último elementos respectivamente.




Programación Avanzada - Tema 4: Programación Genérica – 42
4.2.2 Iteradores (II)


        i n t main ( ) {
            vector <int > v i ;
            / / . . . rellenamos v i                                                       vi
                                                                                      vector<int>
            i f ( f i n d ( v i . begin ( ) , v i . end ( ) , 7 ) )
               // ...                                                                          ...


            l i s t <int > l i ;
                                                                           vi.begin()                vi.end()
            / / . . . rellenamos l i                                  vector<int>::iterator    vector<int>::iterator
            i f ( f i n d ( l i . begin ( ) , l i . end ( ) , 7 ) )
               // ...                                                                      li
        }                                                                              list<int>




  ® Aquí usamos una nueva versión de
                                                                            li.begin()                    li.end()
        find() que utiliza iteradores en lugar                          list<int>::iterator           list<int>::iterator

        de punteros. Implementaremos esta
        versión más adelante.

Programación Avanzada - Tema 4: Programación Genérica – 43
4.2.2 Iteradores (III)

  ® Los iteradores son objetos que
                                                             # include < iostream >
        soportan el mismo conjunto de
                                                             # include < l i s t >
        operaciones que los punteros: ++,                    u s i g n namespace s t d ;

        *, == y != pero tienen una
                                                             i n t main ( ) {
        implementación diferente para cada                       i n t primos [ 5 ] = { 1 , 3 , 5 , 7 , 1 1 } ;
        contenedor.
                                                                 l i s t < i n t > l i ( primos , primos +5) ;
  ® En el ejemplo de la derecha,                                 l i s t < i n t > : : i t e r a t o r i t e r = l i . begin ( ) ;
                                                                 c o u t < < "{ " ;
        recorremos una lista usando un
                                                                 while ( i t e r ! = l i . end ( ) ) {
        iterador.                                                     cout < < ∗ i t e r < < " " ;
                                                                     ++ i t e r ;
  ® Observa que creamos el contendor li                          }
        usando un par de iteradores (en este                     c o u t < < "}" < < e n d l ;
                                                             }
        caso, punteros) sobre un array.

                                                             { 1 3 5 7 11 }

Programación Avanzada - Tema 4: Programación Genérica – 44
4.2.2 Iteradores (IV)

  ® Volviendo a nuestra función find(), aquí tenemos la versión definitiva, válida para
        todo tipo de contenedor secuencial y cualquier array : hemos conseguido un
        algoritmo genérico usando iteradores como abstracción del concepto de puntero:
        template < class I t e r a d o r , class T>
        I t e r a d o r f i n d ( I t e r a d o r f i r s t , I t e r a d o r l a s t , const T & v a l o r ) {
            while ( f i r s t ! = l a s t ) {
                i f ( ∗ f i r s t == valor )
                   return f i r s t ;
                f i r s t ++;
            }
            return l a s t ;
        }



  ® Atención: Esta función find() es uno de los algoritmos genéricos de la biblioteca
        estándar.


Programación Avanzada - Tema 4: Programación Genérica – 45
4.2.2 Iteradores (V)


 i n t main ( ) {                                                      $ g++ − o prueba prueba . cpp
     i n t primos [ 5 ] = { 1 , 3 , 5 , 7 , 1 1 } ;                    $ prueba
     int ∗ ptr ;                                                       Encontrado 7
     p t r = f i n d ( primos , primos + 5 , 7 ) ;                     No encontrado b o n j o u r
     i f ( p t r ! = primos +5)
        c o u t < < "Encontrado 7" < < e n d l ;
     else
        c o u t < < "No encontrado 7" < < e n d l ;


     l i s t <string > ls ;
     l s . p u s h _ f r o n t ( "hola" ) ;
     l s . p u s h _ f r o n t ( "hello" ) ;
     l i s t <string >:: i te r at or          it ;
     i t = f i n d ( l s . begin ( ) , l s . end ( ) , "bonjour" ) ;
     i f ( i t ! = l s . end ( ) )
        cout <<"Encontrado bonjour"<< e n d l ;
     else
        cout <<"No encontrado bonjour"<< e n d l ;
 }

Programación Avanzada - Tema 4: Programación Genérica – 46
4.2.3 Algoritmos genéricos en la biblioteca STL

 En la STL hay definidos más de 60 algoritmos genéricos:

  ® Algoritmos de búsqueda: find(), binary_search(), count(), search(), find_if(),
        count_if(), . . .

  ® Algoritmos de ordenación: sort(), partial_sort(), merge(), reverse(), rotate(), . . .

  ® Algoritmos de copiado, borrado y sustitución: copy(), remove(), remove_if(),
        replace(), swap(), . . .

  ® Algoritmos relacionales: equal(), includes(), mismatch(), . . .

  ® Algoritmos de generación y transformación: fill(), for_each(), generate(),
        transform(), . . .

  ® Algoritmos numéricos: accumulate(), partial_sum(), inner_product(), . . .

  ® Algoritmos sobre conjuntos: set_union(), set_difference(), . . .
Programación Avanzada - Tema 4: Programación Genérica – 47
4.2.4 Ejemplos de utilización de algoritmos genéricos

  ® Para utilizar los algoritmos genéricos hay que incluir el fichero de cabecera
    correspondiente: #include <algorithm>

  ® Supongamos, por ejemplo, que necesitamos implementar la función is_elem() de
        modo que devuelva true si un número dado es un elemento de un vector ordenado
        de enteros. Podemos usar cuatro posibles algoritmos genéricos:
        1. find() busca un valor en una colección no ordenada marcada con dos
             iteradores first y last. Si se encuentra el valor, devuelve un iterador al valor. En
             caso contrario, devuelve last.
        2. binary_search() busca en una colección ordenada. Devuelve true o false si
             encuentra el valor o no. Es más eficiente que find().
        3. count() devuelve el número de repeticiones de un valor.
        4. search() busca una subsecuencia en una secuencia. Devuelve un iterador al
             principio de la subsecuencia.
Programación Avanzada - Tema 4: Programación Genérica – 48
4.2.4 Ejemplos de utilización de algoritmos genéricos (II)

  ® Una posible implementación de la función is_elem() puede usar binary_search(),
        ya que tenemos la garantía de que la secuencia de números está ordenada.
        # include < a l g o r i t h m >
        # include < v e c t o r >
        # include < iostream >


        using namespace s t d ;


        bool is_elem ( const v e c t o r < i n t > & v , i n t v a l o r ) {
            r e t u r n b i n a r y _ s e a r c h ( v . begin ( ) , v . end ( ) , v a l o r ) ;
        }


        i n t main ( ) {
            int array [8]={1 ,2 ,3 ,5 ,7 ,11 ,13 ,17};


            v e c t o r < i n t > primos ( a r r a y , a r r a y +8) ;


            i f ( is_elem ( primos , 1 1 ) )
               c o u t < < "11 esta en primos" ;
            else
               c o u t < < "11 no esta en primos" ;
        }




Programación Avanzada - Tema 4: Programación Genérica – 49
4.2.4 Ejemplos de utilización de algoritmos genéricos (III)

  ® La función is_elem(), como hemos visto, supone que el vector de enteros que
        recibe está ordenado. Pero, ¿y si no está garantizado?

  ® Podemos escribir una nueva versión que hace una copia del contenedor, la ordena
        y sobre la versión ordenada realiza la búsqueda:
        # include < a l g o r i t h m >
        # include < v e c t o r >
        # include < iostream >


        using namespace s t d ;


        bool is_elem ( const v e c t o r < i n t > & v , i n t v a l o r ) {
            vector <int > copia ( v . size ( ) ) ;


            copy ( v . begin ( ) , v . end ( ) , c o p i a . begin ( ) ) ;
            s o r t ( c o p i a . begin ( ) , c o p i a . end ( ) ) ;
            r e t u r n b i n a r y _ s e a r c h ( c o p i a . begin ( ) , c o p i a . end ( ) , v a l o r ) ;
        }


Programación Avanzada - Tema 4: Programación Genérica – 50
Fin
   Copyright c 2010 José Luis Llopis Borrás

Realizada con ujislides c 2002-3 Sergio Barrachina (barrachi@icc.uji.es)

Más contenido relacionado

La actualidad más candente (20)

Diagrama de clases
Diagrama de clasesDiagrama de clases
Diagrama de clases
 
Curso Java Inicial 5 Relaciones Entre Objetos
Curso Java Inicial   5 Relaciones Entre ObjetosCurso Java Inicial   5 Relaciones Entre Objetos
Curso Java Inicial 5 Relaciones Entre Objetos
 
Poo clases y relaciones clase04
Poo clases y relaciones clase04Poo clases y relaciones clase04
Poo clases y relaciones clase04
 
JAVA PROGRAMMING - The Collections Framework
JAVA PROGRAMMING - The Collections Framework JAVA PROGRAMMING - The Collections Framework
JAVA PROGRAMMING - The Collections Framework
 
String.ppt
String.pptString.ppt
String.ppt
 
Encapsulamiento
EncapsulamientoEncapsulamiento
Encapsulamiento
 
Arrays in java
Arrays in javaArrays in java
Arrays in java
 
Ejercicios resueltos de java
Ejercicios resueltos de javaEjercicios resueltos de java
Ejercicios resueltos de java
 
Arreglos de registros
Arreglos de registros Arreglos de registros
Arreglos de registros
 
Java ArrayList Tutorial | Edureka
Java ArrayList Tutorial | EdurekaJava ArrayList Tutorial | Edureka
Java ArrayList Tutorial | Edureka
 
POO - 14 - Vetores
POO - 14 - VetoresPOO - 14 - Vetores
POO - 14 - Vetores
 
Pointers in c++
Pointers in c++Pointers in c++
Pointers in c++
 
Base de datos con Netbeans
Base de datos con NetbeansBase de datos con Netbeans
Base de datos con Netbeans
 
16 Curso de POO en java - arreglos unidimensionales
16 Curso de POO en java - arreglos unidimensionales16 Curso de POO en java - arreglos unidimensionales
16 Curso de POO en java - arreglos unidimensionales
 
Infografia
InfografiaInfografia
Infografia
 
Arrays em java
Arrays em javaArrays em java
Arrays em java
 
Diagramas uml
Diagramas uml Diagramas uml
Diagramas uml
 
Unidad iii pilas y colas
Unidad iii pilas y colasUnidad iii pilas y colas
Unidad iii pilas y colas
 
Programacion orientada a objetos Java
Programacion orientada a objetos JavaProgramacion orientada a objetos Java
Programacion orientada a objetos Java
 
Manipulating strings
Manipulating stringsManipulating strings
Manipulating strings
 

Destacado (20)

SOMOS MUSICOS
SOMOS MUSICOSSOMOS MUSICOS
SOMOS MUSICOS
 
Cruzadas
CruzadasCruzadas
Cruzadas
 
Instrumento de diseño curricular didáctico cielo maria correa
Instrumento de diseño curricular didáctico  cielo maria correaInstrumento de diseño curricular didáctico  cielo maria correa
Instrumento de diseño curricular didáctico cielo maria correa
 
Expo farmaco1
Expo farmaco1Expo farmaco1
Expo farmaco1
 
Stephanie España2
Stephanie España2Stephanie España2
Stephanie España2
 
Presentacion pnuma2
Presentacion pnuma2Presentacion pnuma2
Presentacion pnuma2
 
Leishmaniasis
LeishmaniasisLeishmaniasis
Leishmaniasis
 
Nuestro gigante virtual proyecto 2
Nuestro gigante virtual   proyecto 2Nuestro gigante virtual   proyecto 2
Nuestro gigante virtual proyecto 2
 
Rss Luis Gil
Rss Luis GilRss Luis Gil
Rss Luis Gil
 
Cuestionario
CuestionarioCuestionario
Cuestionario
 
Biomoleculas
BiomoleculasBiomoleculas
Biomoleculas
 
Programas radiofónicos en la educación
Programas radiofónicos en la educaciónProgramas radiofónicos en la educación
Programas radiofónicos en la educación
 
Maria Augusta Garcia
Maria Augusta GarciaMaria Augusta Garcia
Maria Augusta Garcia
 
Seminario nº5
Seminario nº5Seminario nº5
Seminario nº5
 
Comic xela copy
Comic xela copyComic xela copy
Comic xela copy
 
Fotografía
FotografíaFotografía
Fotografía
 
Mi eportafolio
Mi eportafolioMi eportafolio
Mi eportafolio
 
Gluconeogenesis
GluconeogenesisGluconeogenesis
Gluconeogenesis
 
Actividades diversas en el colegio
Actividades diversas  en el colegioActividades diversas  en el colegio
Actividades diversas en el colegio
 
Sena tecno parque
Sena tecno parque Sena tecno parque
Sena tecno parque
 

Similar a Tema4 programación generica

Analisis Clase2
Analisis  Clase2Analisis  Clase2
Analisis Clase2luzenith_g
 
Funciones con vectores y matrices
Funciones con vectores y matricesFunciones con vectores y matrices
Funciones con vectores y matricesJohanna Marin
 
Funciones con vectores y matrices
Funciones con vectores y matricesFunciones con vectores y matrices
Funciones con vectores y matricesJohanna Marin
 
Algoritmos - Funciones C++
Algoritmos - Funciones C++ Algoritmos - Funciones C++
Algoritmos - Funciones C++ Ronal Palomino
 
Fundamentos de Programación - Unidad IV: Arreglos (Vectores)
Fundamentos de Programación - Unidad IV: Arreglos (Vectores)Fundamentos de Programación - Unidad IV: Arreglos (Vectores)
Fundamentos de Programación - Unidad IV: Arreglos (Vectores)José Antonio Sandoval Acosta
 
Fundamentos de Programacion - Unidad 5 arreglos (vectores)
Fundamentos de Programacion - Unidad 5 arreglos (vectores)Fundamentos de Programacion - Unidad 5 arreglos (vectores)
Fundamentos de Programacion - Unidad 5 arreglos (vectores)José Antonio Sandoval Acosta
 
Arreglo unidimensionales y bidimensionales
Arreglo unidimensionales y bidimensionalesArreglo unidimensionales y bidimensionales
Arreglo unidimensionales y bidimensionalesMarco Garay
 
3 desarollo manejo datos capitulo 2 -01 arreglos dos dimensiones (2)
3 desarollo manejo datos capitulo 2 -01 arreglos dos dimensiones (2)3 desarollo manejo datos capitulo 2 -01 arreglos dos dimensiones (2)
3 desarollo manejo datos capitulo 2 -01 arreglos dos dimensiones (2)luis freddy
 
3 desarollo manejo datos capitulo 2 -01 arreglos dos dimensiones (5)
3 desarollo manejo datos capitulo 2 -01 arreglos dos dimensiones (5)3 desarollo manejo datos capitulo 2 -01 arreglos dos dimensiones (5)
3 desarollo manejo datos capitulo 2 -01 arreglos dos dimensiones (5)luis freddy
 
El lenguaje de programacion c++ prev
El lenguaje de programacion c++ prevEl lenguaje de programacion c++ prev
El lenguaje de programacion c++ prevjtk1
 
Curva de daño de un transformador c++
Curva de daño de un transformador c++Curva de daño de un transformador c++
Curva de daño de un transformador c++Marco Jiménez
 
Introducción a C SHARP
Introducción a C SHARPIntroducción a C SHARP
Introducción a C SHARPinfobran
 
Sesion1_Ciencia_de_Datos-Introduccion a Pithon.pdf
Sesion1_Ciencia_de_Datos-Introduccion a Pithon.pdfSesion1_Ciencia_de_Datos-Introduccion a Pithon.pdf
Sesion1_Ciencia_de_Datos-Introduccion a Pithon.pdfMarxx4
 

Similar a Tema4 programación generica (20)

Analisis Clase2
Analisis  Clase2Analisis  Clase2
Analisis Clase2
 
Funciones con vectores y matrices
Funciones con vectores y matricesFunciones con vectores y matrices
Funciones con vectores y matrices
 
Funciones con vectores y matrices
Funciones con vectores y matricesFunciones con vectores y matrices
Funciones con vectores y matrices
 
Algoritmos - Funciones C++
Algoritmos - Funciones C++ Algoritmos - Funciones C++
Algoritmos - Funciones C++
 
Fundamentos de Programación - Unidad IV: Arreglos (Vectores)
Fundamentos de Programación - Unidad IV: Arreglos (Vectores)Fundamentos de Programación - Unidad IV: Arreglos (Vectores)
Fundamentos de Programación - Unidad IV: Arreglos (Vectores)
 
Fundamentos de Programacion - Unidad 5 arreglos (vectores)
Fundamentos de Programacion - Unidad 5 arreglos (vectores)Fundamentos de Programacion - Unidad 5 arreglos (vectores)
Fundamentos de Programacion - Unidad 5 arreglos (vectores)
 
Cpp
CppCpp
Cpp
 
Cpp
CppCpp
Cpp
 
C sharp intro0
C sharp intro0C sharp intro0
C sharp intro0
 
C sharp intro0
C sharp intro0C sharp intro0
C sharp intro0
 
Arreglo unidimensionales y bidimensionales
Arreglo unidimensionales y bidimensionalesArreglo unidimensionales y bidimensionales
Arreglo unidimensionales y bidimensionales
 
3 desarollo manejo datos capitulo 2 -01 arreglos dos dimensiones (2)
3 desarollo manejo datos capitulo 2 -01 arreglos dos dimensiones (2)3 desarollo manejo datos capitulo 2 -01 arreglos dos dimensiones (2)
3 desarollo manejo datos capitulo 2 -01 arreglos dos dimensiones (2)
 
3 desarollo manejo datos capitulo 2 -01 arreglos dos dimensiones (5)
3 desarollo manejo datos capitulo 2 -01 arreglos dos dimensiones (5)3 desarollo manejo datos capitulo 2 -01 arreglos dos dimensiones (5)
3 desarollo manejo datos capitulo 2 -01 arreglos dos dimensiones (5)
 
El lenguaje de programacion c++ prev
El lenguaje de programacion c++ prevEl lenguaje de programacion c++ prev
El lenguaje de programacion c++ prev
 
Curva de daño de un transformador c++
Curva de daño de un transformador c++Curva de daño de un transformador c++
Curva de daño de un transformador c++
 
Introducción a Python
Introducción a PythonIntroducción a Python
Introducción a Python
 
Introducción a C SHARP
Introducción a C SHARPIntroducción a C SHARP
Introducción a C SHARP
 
Viernes Tecnicos DTrace
Viernes Tecnicos DTraceViernes Tecnicos DTrace
Viernes Tecnicos DTrace
 
8 b refactoring
8 b refactoring8 b refactoring
8 b refactoring
 
Sesion1_Ciencia_de_Datos-Introduccion a Pithon.pdf
Sesion1_Ciencia_de_Datos-Introduccion a Pithon.pdfSesion1_Ciencia_de_Datos-Introduccion a Pithon.pdf
Sesion1_Ciencia_de_Datos-Introduccion a Pithon.pdf
 

Más de Pedro Hugo Valencia Morales (10)

Árboles como Estructura de Datos
Árboles como Estructura de DatosÁrboles como Estructura de Datos
Árboles como Estructura de Datos
 
C++ io manipulation
C++ io manipulationC++ io manipulation
C++ io manipulation
 
Colas de prioridad
Colas de prioridadColas de prioridad
Colas de prioridad
 
Heaps & priority queues
Heaps & priority queuesHeaps & priority queues
Heaps & priority queues
 
Arboles03
Arboles03Arboles03
Arboles03
 
Arboles02
Arboles02Arboles02
Arboles02
 
Arboles01
Arboles01Arboles01
Arboles01
 
Arquitectura ssdd
Arquitectura ssddArquitectura ssdd
Arquitectura ssdd
 
Cap02 modelos1
Cap02 modelos1Cap02 modelos1
Cap02 modelos1
 
Chapter 1 slides
Chapter 1 slidesChapter 1 slides
Chapter 1 slides
 

Tema4 programación generica

  • 1. Programación Avanzada - Tema 4: Programación Genérica http://aulavirtual.uji.es José Luis Llopis Borrás 22 de septiembre de 2010
  • 2. Índice 1. Introducción 4 2. Funciones genéricas 5 2.1. Definición e instanciación . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.2. Restricciones de uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.3. Especialización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.4. Sobrecarga . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 3. Clases genéricas 16 3.1. Definición de métodos . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.2. Organización del código fuente . . . . . . . . . . . . . . . . . . . . . . 21 3.3. Parámetros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 3.4. Herencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 4. Introducción a la STL 32 4.1. Contenedores secuenciales . . . . . . . . . . . . . . . . . . . . . . . . 33
  • 3. 4.1.1. Instanciación . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 4.1.2. Operaciones comunes . . . . . . . . . . . . . . . . . . . . . . . 37 4.2. Algoritmos genéricos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 4.2.1. Aritmética de punteros . . . . . . . . . . . . . . . . . . . . . . . 40 4.2.2. Iteradores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 4.2.3. Algoritmos genéricos en la biblioteca STL . . . . . . . . . . . . . 47 4.2.4. Ejemplos de utilización de algoritmos genéricos . . . . . . . . . . 48
  • 4. 1 Introducción ® Ciertos algoritmos son independientes del tipo de datos sobre los que operan. µ Ejemplo: el cálculo del menor de dos valores enteros es idéntico al cálculo del menor (alfabéticamente) de dos strings: int min(int a, int b) { return a<b ? a : b; } string min(string a, string b) { return a<b ? a : b; } µ Implementaremos estos algoritmos con funciones genéricas. ® Ciertos tipos de datos pueden definirse de forma independiente (de al menos alguno) de los tipos de datos que manipulan. µ Ejemplo: la implementación de una lista de enteros sólo difiere de la de una lista de strings en el tipo de información que almacena. µ Implementaremos estos tipos de datos con clases genéricas. ® La genericidad es un potente mecanismo que permite la reutilización de código. Programación Avanzada - Tema 4: Programación Genérica – 4
  • 5. 2 Funciones genéricas ® Supongamos que debemos implementar una función que imprima en la salida estándar los valores de un array de enteros: void i m p r i m i r ( i n t v [ ] , i n t t a l l a ) { c o u t < < "[ " ; f o r ( i n t i = 0 ; i < t a l l a ; i ++) cout < < v [ i ] < < " " ; c o u t < < "]" < < e n d l ; } i n t main ( ) { i n t v1 [ 5 ] = { 5 , 2 , 3 , 4 , 1 } ; i m p r i m i r ( v1 , 5 ) } [ 5 2 3 4 1 ] Programación Avanzada - Tema 4: Programación Genérica – 5
  • 6. 2 Funciones genéricas (II) ® ...y más tarde necesitamos implementar otra función que imprima los valores de un array de números reales: void i m p r i m i r ( f l o a t v [ ] , i n t t a l l a ) { c o u t < < "[ " ; f o r ( i n t i = 0 ; i < t a l l a ; i ++) cout < < v [ i ] < < " " ; c o u t < < "]" < < e n d l ; } i n t main ( ) { f l o a t v2 [ 3 ] = { 3 . 3 , 1 , 0 . 9 9 } ; i m p r i m i r ( v2 , 3 ) } [ 3.3 1 0.99 ] ® Las dos versiones sólo se diferencian en el tipo base del array. Programación Avanzada - Tema 4: Programación Genérica – 6
  • 7. 2.1 Definición e instanciación ® Implementamos una función genérica como única versión: t e m p l a t e < class T > void i m p r i m i r ( T v [ ] , i n t t a l l a ) { c o u t < < "[ " ; f o r ( i n t i = 0 ; i < t a l l a ; i ++) cout < < v [ i ] < < " " ; c o u t < < "]" < < e n d l ; } i n t main ( ) { i n t v1 [ 5 ] = { 5 , 2 , 3 , 4 , 1 } ; f l o a t v2 [ 3 ] = { 3 . 3 , 1 , 0 . 9 9 } ; i m p r i m i r ( v1 , 5 ) ; i m p r i m i r ( v2 , 3 ) ; } [ 5 2 3 4 1 ] [ 3.3 1 0.99 ] Programación Avanzada - Tema 4: Programación Genérica – 7
  • 8. 2.1 Definición e instanciación (II) ® El compilador utiliza el código de la función genérica como plantilla para crear automáticamente dos funciones sustituyendo T por un tipo concreto: µ Con T=int se crea la función void imprimir(int v[], int talla) para satisfacer la llamada a imprimir(v1, 5). µ Con T=float se crea la función void imprimir(float v[], int talla) para satisfacer la llamada a imprimir(v2, 3). ® Esta operación recibe el nombre de instanciación. Programación Avanzada - Tema 4: Programación Genérica – 8
  • 9. 2.1 Definición e instanciación (III) ® Vamos a añadir una función genérica i n t main ( ) { que permita la ordenación de arrays: i n t v1 [ 5 ] = { 5 , 2 , 3 , 4 , 1 } ; ordenar ( v1 , 5 ) ; / / T= i n t t e m p l a t e < class T> i m p r i m i r ( v1 , 5 ) ; / / T= i n t void i n t e r c a m b i a r ( T & ip1 , T & i p 2 ) { T aux ; f l o a t v2 [ 3 ] = { 3 . 3 , 1 . 0 , 0 . 9 9 } ; aux = i p 1 ; ordenar ( v2 , 3 ) ; / / T= f l o a t ip1 = ip2 ; i m p r i m i r ( v2 , 3 ) ; / / T= f l o a t i p 2 = aux ; } char v3 [ 5 ] = { ’e’ , ’o’ , ’a’ , ’u’ , ’i’ } ; / / ordenación ascendente método b u r b u j a ordenar ( v3 , 5 ) ; / / T=char t e m p l a t e < class T> i m p r i m i r ( v3 , 5 ) ; / / T=char void ordenar ( T v [ ] , i n t t a l l a ) { } f o r ( i n t i = t a l l a − 1; i > = 1 ; i −−) f o r ( i n t j = 0 ; j < i ; j ++) [ 1 2 3 4 5 ] i f ( v [ j ] > v [ j +1]) [ 0.99 1 3.3 ] intercambiar ( v [ j ] , v [ j +1]) ; [ a e i o u ] } Programación Avanzada - Tema 4: Programación Genérica – 9
  • 10. 2.1 Definición e instanciación (IV) ® En la implementación de la función genérica imprimir() la única operación que realizamos sobre valores de tipo T es la de salida de datos: cout << v[i] µ El compilador necesita de esa operación para crear instancias de la función. µ Esto impone una restricción sobre las posibles instancias: sólo se admitirán aquellos tipos T para los que la operación de salida << esté definida. ® ¿Qué restricciones impone las funciones genéricas ordenar() e intercambiar()? µ En la función ordenar() se necesita el operador > entre valores de tipo T. µ En la función intercambiar() se necesita que el operador = (asignación) esté correctamente definido entre valores de tipo T. µ En la función intercambiar() se necesita que un constructor sin argumentos que permita crear objetos de tipo T (constructor por omisión). Programación Avanzada - Tema 4: Programación Genérica – 10
  • 11. 2.2 Restricciones de uso ® Teniendo en cuenta las restricciones mencionadas antes, ¿podemos instanciar imprimir() y ordenar() para arrays de objetos de la clase Cadena (tal y como la definimos en el Tema 2)? Cadena v4 [ 3 ] = { "tigre" , "gato" , "leon" } ; ordenar ( v4 , 3 ) ; i m p r i m i r ( v4 , 3 ) ; ® Para la función imprimir() no hay problema puesto que existe la función: ostream & operator < < ( ostream & canal , const Cadena & c ) ; ® Para la función intercambiar() tampoco, puesto que la asignación entre cadenas está definida: Cadena & Cadena : : operator = ( const Cadena & c ) ; Programación Avanzada - Tema 4: Programación Genérica – 11
  • 12. 2.2 Restricciones de uso (II) ® Sin embargo, la función ordenar() necesita una función no definida en la clase Cadena: bool Cadena : : operator > ( const Cadena & c ) const ; ® El compilador nos informa del error: t e s t . cpp : I n f u n c t i o n ‘ void ordenar ( T ∗ , i n t ) [ w i t h T = Cadena ] ’: test.cpp:247: instantiated from here test.cpp:218: no match for ‘Cadena& > Cadena&’ operator Programación Avanzada - Tema 4: Programación Genérica – 12
  • 13. 2.3 Especialización ® Para solucionar el problema, definimos una nueva versión no genérica de ordenar() para arrays de objetos de tipo Cadena. Este proceso recibe el nombre de especialización. / / e s p e c i a l i z a c i ó n para a r r a y s de cadenas void ordenar ( Cadena v [ ] , i n t t a l l a ) { f o r ( i n t i = t a l l a − 1; i > = 1 ; i −−) f o r ( i n t j = 0 ; j < i ; j ++) i f ( strcmp ( v [ j ] . s t r , v [ j + 1 ] . s t r ) >0) intercambiar ( v [ j ] , v [ j +1]) ; } i n t main ( ) { Cadena v4 [ 3 ] = { "tigre" , "gato" , "leon" } ; ordenar ( v4 , 3 ) ; i m p r i m i r ( v4 , 3 ) ; } [ gato l e o n t i g r e ] Programación Avanzada - Tema 4: Programación Genérica – 13
  • 14. 2.3 Especialización (II) ® Observa que en la función ordenar() accedemos a la parte privada de la clase Cadena en las expresiones v[j].str y v[j+1].str. Por tanto deberemos declarar la función ordenar() como friend de Cadena. ® En este ejemplo concreto, podemos optar por una segunda solución que no necesita especializar la función genérica ordenar(): podemos simplemente dotar a la clase Cadena del operador >. class Cadena { ... public : ... bool operator >( const Cadena & s ) const ; }; bool Cadena : : operator >( const Cadena & s ) const { r e t u r n ( strcmp ( s t r , s . s t r ) >0) ? t r u e : f a l s e ; } Programación Avanzada - Tema 4: Programación Genérica – 14
  • 15. 2.4 Sobrecarga ® Las funciones genéricas pueden ser template < class T> sobrecargadas de la misma forma T min ( T a , T b ) { que las funciones ordinarias. r e t u r n ( a<b ? a : b ) ; } µ Podemos especificar más de un tipo genérico (segundo ejemplo template < class T1 , class T2> de la derecha). T1 min ( T1 a , T2 b ) { // ... µ Al menos alguno de los } parámetros de la función debe ser genérico, si no, el compilador no template < class T > / / i n c o r r e c t o puede saber cómo instanciar el T min ( i n t a , i n t b ) { r e t u r n ( a<b ? a : b ) ; tipo T (tercer ejemplo de la } derecha). Programación Avanzada - Tema 4: Programación Genérica – 15
  • 16. 3 Clases genéricas Una clase genérica es una plantilla de definición de una clase que puede ser instanciada en múltiples versiones concretas. ® Declaramos la clase genérica Vector que almacena valores de tipo T: t e m p l a t e < class T> class V e c t o r { T ∗ v; int t a l l a ; public : Vector ( i n t ) ; V e c t o r ( const Vector <T> & ) ; ~Vector ( ) ; Vector <T > & operator =( const Vector <T > & ) ; T & operator [ ] ( i n t ) ; void i m p r i m i r ( ) const ; i n t T a l l a ( ) const { r e t u r n t a l l a ; } }; Programación Avanzada - Tema 4: Programación Genérica – 16
  • 17. 3 Clases genéricas (II) ® Podemos instanciar la clase Vector para T=int o T=Cadena: i n t main ( ) { Vector < i n t > v I n t ( 3 ) ; / / I n s t a n c i a c i ó n : T= i n t f o r ( i n t i = 0 ; i < v I n t . T a l l a ( ) ; i ++) cin > > vInt [ i ] ; vInt . imprimir () ; Vector <Cadena > vCad ( 3 ) ; / / I n s t a n c i a c i ó n : T=Cadena f o r ( i n t i = 0 ; i <vCad . T a l l a ( ) ; i ++) c i n > > vCad [ i ] ; vCad . i m p r i m i r ( ) ; } 3 4 5 [ 3 4 5 ] h o l a que t a l [ h o l a que t a l ] Programación Avanzada - Tema 4: Programación Genérica – 17
  • 18. 3.1 Definición de métodos ® Las funciones miembro de una clase genérica son, a su vez, funciones genéricas. ® Atención: la clase Vector no existe como tal, sólo es una plantilla. Hay que escribir, por tanto, Vector<T>. t e m p l a t e < class T > / / c o n s t r u c t o r Vector <T > : : V e c t o r ( i n t t ) { talla = t ; v=new T [ t a l l a ] ; } t e m p l a t e < class T > / / c o n s t r u c t o r c o p i a Vector <T > : : V e c t o r ( const Vector <T > & unVector ) { t a l l a = unVector . t a l l a ; v = new T [ t a l l a ] ; f o r ( i n t i = 0 ; i < t a l l a ; i ++) v [ i ] = unVector . v [ i ] ; } Programación Avanzada - Tema 4: Programación Genérica – 18
  • 19. 3.1 Definición de métodos (II) t e m p l a t e < class T > / / d e s t r u c t o r Vector <T > : : ~ V e c t o r ( ) { delete [ ] v ; } t e m p l a t e < class T > / / operador a s i g n a c i ó n Vector <T > & Vector <T > : : operator =( const Vector <T > & unVector ) { i f ( t h i s ! = & unVector ) { i f ( t a l l a ! = unVector . t a l l a ) { delete [ ] v ; t a l l a = unVector . t a l l a ; v = new T [ t a l l a ] ; } f o r ( i n t i = 0 ; i < t a l l a ; i ++) v [ i ] = unVector . v [ i ] ; } return ∗ this ; } Programación Avanzada - Tema 4: Programación Genérica – 19
  • 20. 3.1 Definición de métodos (III) t e m p l a t e < class T > / / operador s u b í n d i c e T & Vector <T > : : operator [ ] ( i n t i ) { return v [ i ] ; } t e m p l a t e < class T > / / f u n c i ó n i m p r i m i r ( ) void Vector <T > : : i m p r i m i r ( ) const { c o u t < < "[ " ; f o r ( i n t i = 0 ; i < t a l l a ; i ++) cout < < v [ i ] < < " " ; c o u t < < "]" < < e n d l ; } Programación Avanzada - Tema 4: Programación Genérica – 20
  • 21. 3.2 Organización del código fuente El modelo tradicional de organización de código fuente —modelo de compilación Listado 1: MiClaseGenerica.h separada—, no funciona con módulos template <class T> que contienen clases genéricas. class MiClaseGenerica { private : ® Recuerda que este modelo T dato ; public : consistente en separar la declaración MiClaseGenerica ( T ) ; y la implementación de un módulo void i m p r i m i r ( ) const ; (clase) en dos ficheros separados, }; con extensiones .h y .cpp respectivamente. ® Veamos el ejemplo de la clase MiClaseGenerica a continuación. Programación Avanzada - Tema 4: Programación Genérica – 21
  • 22. 3.2 Organización del código fuente (II) Listado 2: MiClaseGenerica.cpp Listado 3: TestMiClaseGenerica.cpp # include "MiClaseGenerica.h" # include "MiClaseGenerica.h" # include < iostream > using namespace s t d ; i n t main ( ) { MiClaseGenerica < i n t > a(123) ; template < class T> MiClaseGenerica < f l o a t > b ( 3 . 1 4 1 6 ) ; MiClaseGenerica <T > : : MiClaseGenerica ( T x ) MiClaseGenerica <char > c ( ’#’ ) ; : dato ( x ) { } a . imprimir () ; b . imprimir () ; template < class T> c . imprimir () ; void MiClaseGenerica <T > : : i m p r i m i r ( ) const { } c o u t < < dato < < e n d l ; } Programación Avanzada - Tema 4: Programación Genérica – 22
  • 23. 3.2 Organización del código fuente (III) ® Efectivamente, ¡la compilación falla! Listado 4: Resultado de la compilación $ g++ − c MiClaseGenerica . cpp $ g++ − c TestMiClaseGenerica . cpp $ g++ − o Test MiClaseGenerica . o TestMiClaseGenerica . o / u s r / b i n / l d : Undefined symbols : MiClaseGenerica <char > : : MiClaseGenerica ( char ) MiClaseGenerica < f l o a t > : : MiClaseGenerica ( f l o a t ) MiClaseGenerica < i n t > : : MiClaseGenerica ( i n t ) MiClaseGenerica <char > : : i m p r i m i r ( ) const MiClaseGenerica < f l o a t > : : i m p r i m i r ( ) const MiClaseGenerica < i n t > : : i m p r i m i r ( ) const c o l l e c t 2 : ld returned 1 e x i t status Programación Avanzada - Tema 4: Programación Genérica – 23
  • 24. 3.2 Organización del código fuente (IV) ® ¿Por qué falla el modelo de compilación separada? µ Un clase genérica (template) no es una clase, sino un patrón que el compilador usa para generar una o más clases. µ Para que el compilador pueda generar código, debe ver a la vez la implementación de la clase (no sólo la declaración) y los tipos específicos usados para instanciar el patrón. ª Si deseamos usar MiClaseGenerica<int>, el compilador necesita ver a la vez la implementación de la clase MiClaseGenerica<T> y el lugar donde instanciamos T como int. ª La implementación de MiClaseGenerica<T> está en un fichero y el lugar donde instanciamos T en otro fichero. Ambos ficheros se compilan separadamente y, por tanto, el compilador no sabe que tipo concreto es T cuando compila la clase genérica, así que no genera ningún código. Programación Avanzada - Tema 4: Programación Genérica – 24
  • 25. 3.2 Organización del código fuente (V) ® Existen 3 soluciones al problema: 1. Mover físicamente la implementación de la clase al fichero .h, con lo que declaración e implementación estarán en el mismo lugar. 2. (Recomendada) Dejar por separado declaración e implementación, pero incluir la implementación (fichero .cpp) donde se realiza la instanciación: TestMiClaseGenerica.cpp # include "MiClaseGenerica.cpp" ... 3. Utilizar la palabra reservada export. Hacer un compilador que la soporte es muy complicado. Sólo uno lo ha hecho: Comeau C++, pero no GCC. MiClaseGenerica.h export template <class T > class MiClaseGenerica { ... Programación Avanzada - Tema 4: Programación Genérica – 25
  • 26. 3.3 Parámetros ® Las clases genéricas pueden tomar dos tipos de parámetros: µ Tipos genéricos, como los identificadores T, T1, T2, TipoBase, etc. µ Tipos ordinarios, como los tipos int o char. ® En el siguient ejemplo definimos la clase VectorEstatico para almacener vectores cuya talla es conocida en tiempo de compilación. Clase VectorEstatico: declaración template < i n t tam , class T > / / Parámetros : t i p o o r d i n a r i o i n t y t i p o g e n é r i c o T class V e c t o r E s t a t i c o { T v [ tam ] ; public : T & operator [ ] ( i n t ) ; void i m p r i m i r ( ) const ; }; Programación Avanzada - Tema 4: Programación Genérica – 26
  • 27. 3.3 Parámetros (II) ® ...y aquí está la implementación: Clase VectorEstatico: implementación template < i n t tam , class T> void V e c t o r E s t a t i c o <tam , T > : : i m p r i m i r ( ) const { c o u t < < "[ " ; f o r ( i n t i = 0 ; i <tam ; i ++) cout < < v [ i ] < < " " ; c o u t < < "]" < < e n d l ; } template < i n t tam , class T> T & V e c t o r E s t a t i c o <tam , T > : : operator [ ] ( i n t i ) { return v [ i ] ; } Programación Avanzada - Tema 4: Programación Genérica – 27
  • 28. 3.3 Parámetros (III) ® La instanciación de clases con parámetros constantes se realiza así: i n t main ( ) { V e c t o r E s t a t i c o <3 , i n t > v 3 I n t ; f o r ( i n t i = 0 ; i < 3 ; i ++) cin > > v3Int [ i ] ; v3Int . imprimir () ; V e c t o r E s t a t i c o <5 , i n t > v 5 I n t ; f o r ( i n t i = 0 ; i < 5 ; i ++) cin > > v5Int [ i ] ; v5Int . imprimir () ; } ® Atención: los tipos de las variables v3Int y v5Int son diferentes e incompatibles: µ v3Int es del tipo VectorEstatico de 3 componentes enteras. µ v5Int es del tipo VectorEstatico de 5 componentes enteras. Programación Avanzada - Tema 4: Programación Genérica – 28
  • 29. 3.4 Herencia Podemos derivar clases genéricas de otras clases genéricas. ® Ejemplo: definimos la clase VectorSeguro como especialización de Vector. Ahora el operator[] comprobará si el índice está fuera de rango. Clase VectorSeguro: declaración template < class T> class VectorSeguro : public Vector <T > { public : VectorSeguro ( i n t i ) : Vector <T>( i ) { } T & operator [ ] ( i n t ) ; }; ® Observa que usamos la lista de inicialización para pasar un argumento al constructor de la clase base. Programación Avanzada - Tema 4: Programación Genérica – 29
  • 30. 3.4 Herencia (II) ® Observa que en la sentencia return llamamos a la función operator[ ] de la clase base. Clase VectorSeguro: implementación template < class T> T & VectorSeguro <T > : : operator [ ] ( i n t i ) { i f ( i < 0 | | i > t h i s −>T a l l a ( ) ) { c e r r < < "Error: Indice fuera de rango" < < e n d l ; exit (1) ; } r e t u r n Vector <T > : : operator [ ] ( i ) ; } Programación Avanzada - Tema 4: Programación Genérica – 30
  • 31. 3.4 Herencia (III) ® Probamos la clase VectorSeguro: i n t main ( ) { VectorSeguro < i n t > v ( 3 ) ; f o r ( i n t i = 0 ; i <v . T a l l a ( ) ; i ++) cin > > v [ i ] ; v . imprimir () ; v [10]=33; } 7 8 9 [ 7 8 9 ] E r r o r : I n d i c e f u e r a de rango Programación Avanzada - Tema 4: Programación Genérica – 31
  • 32. 4 Introducción a la STL La biblioteca estándar de plantillas (STL) contiene componentes genéricos de (básicamente) dos tipos: ® Contenedores µ Son clases genéricas que sirven para almacenar (contener) colecciones de elementos de algún tipo. Ejemplos: vector, list, set, map, etc. ® Algoritmos genéricos µ Son operaciones aplicables a los contenedores y los arrays. µ Son independientes del tipo de datos concreto sobre el que operan (int, char, string, . . . ). Por ello están implementadas usando funciones genéricas. µ Son independientes del tipo de contenedor en que los elementos están almacenados (array, list, vector, set, . . . ). Esta independencia se consigue gracias a que no operan directamente sobre ellos, sino a través de iteradores. Programación Avanzada - Tema 4: Programación Genérica – 32
  • 33. 4.1 Contenedores secuenciales Un contenedor secuencial contiene una colección ordenada de elementos de un mismo tipo. Veamos dos de los más importantes: ® vector µ Mantiene sus elementos en un área contigua de memoria. µ El acceso aleatorio es eficiente: es igual de rápido acceder al elemento 5o que al 17o o al 9o . µ El operador de subíndice [ ] permite acceder a sus componentes. µ Sin embargo, la inserción de un elemento en cualquier posición que no sea la última es ineficiente ya que implica el desplazamiento de los elementos a la derecha del insertado. µ Por el mismo motivo, el borrado de un elemento distinto del último es también ineficiente. Programación Avanzada - Tema 4: Programación Genérica – 33
  • 34. 4.1 Contenedores secuenciales (II) ® list µ Mantiene sus elementos en áreas de memoria no contigua (concretamente, nodos doblemente enlazados). µ La inserción y borrado de elementos en cualquier posición es eficiente. µ Sin embargo, el acceso aleatorio es menos eficiente: para acceder al elemento 5o , 17o y 9o hay que visitar todos los intermedios. Programación Avanzada - Tema 4: Programación Genérica – 34
  • 35. 4.1.1 Instanciación ® Para usar un contenedor secuencial, debemos incluir el fichero de cabecera asociado: #include <vector> #include <list> ® Disponemos de cinco modos de instanciar un objeto contenedor secuencial: 1. Crear un contenedor vacío: vector<int> v1; list<Complejo> lista1; 2. Crear un contenedor de algún tamaño no necesariamente constante. Cada elemento se inicializa con el valor por defecto (los int o double con cero): vector<int> v2 (100); list<Cadena> lista2 (talla); Programación Avanzada - Tema 4: Programación Genérica – 35
  • 36. 4.1.1 Instanciación 3. Crear un contenedor de un tamaño y valor inicial dado: vector<string> v3 (16, "una cadena" ); list<float> lista3 (100, 3.1416 ); 4. Crear un contenedor como copia de otro: vector<string> v4 (v3); list<float> lista4 (lista3); 5. Crear un contenedor usando un par de iteradores. (Más adelante volveremos a tratar este asunto). Programación Avanzada - Tema 4: Programación Genérica – 36
  • 37. 4.1.2 Operaciones comunes ® Algunas operaciones comunes: µ ¿Qué talla tiene el contenedor? ¿Está vacío?: size() y empty() µ La asignación, el test de igualdad y desigualdad: =, == y != µ Acceso al primer y último elemento: front() y back() µ Inserción y borrado del último: push_back() y pop_back() ® El contenedor list, pero no el vector, permite además: µ Inserción y borrado del primero: push_front() y pop_front() Programación Avanzada - Tema 4: Programación Genérica – 37
  • 38. 4.1.2 Operaciones comunes (II) Ejemplo de uso Salida # include < iostream > 12 # include < l i s t > 45 using namespace s t d ; 33 111 i n t main ( ) { 35 l i s t <int > miLista ; int valor ; miLista = { 12 45 33 111 35 } while ( c i n > > v a l o r ) m i L i s t a . push_back ( v a l o r ) ; c o u t < < "miLista = { " ; while ( ! m i L i s t a . empty ( ) ) { cout < < miLista . f r o n t ( ) < < ’ ’ ; miLista . pop_front ( ) ; } c o u t < < ’}’ < < e n d l ; } Programación Avanzada - Tema 4: Programación Genérica – 38
  • 39. 4.2 Algoritmos genéricos Supongamos que necesitamos escribir una función que realice la búsqueda secuencial de la primera aparición de un valor determinado en un contenedor vector y devuelva un puntero a él o cero (NULL) si no se encuentra: template < class T > const T ∗ f i n d ( const v e c t o r <T > & v , const T & v a l o r ) { f o r ( i n t i = 0 ; i <v . s i z e ( ) ; i ++) i f ( v [ i ]== v a l o r ) return & v [ i ] ; r e t u r n NULL ; } ® ¿Es posible modificar la función para que también funcione con arrays? ¿Y con otros contenedores secuenciales? ® Si conseguimos estos dos objetivos habremos implementado un algoritmo genérico de búsqueda secuencial aplicable a cualquier contenedor secuencial incluyendo los arrays. El problema radica en encontrar un tratamiento común para arrays y contenedores. Programación Avanzada - Tema 4: Programación Genérica – 39
  • 40. 4.2.1 Aritmética de punteros ® Planteamos una versión de find() para arrays: template < class T> T ∗ f i n d ( T ∗ v , i n t tam , const T & v a l o r ) { i f ( ! v | | tam <1) r e t u r n 0 ; f o r ( i n t i = 0 ; i <tam ; + + i , + + v ) i f ( ∗ v = = v a l o r ) return v ; r e t u r n 0 ; / / NULL } v … ® Esta versión realiza bien su cometido pero hemos 0 1 2 … tam-1 tenido que introducir un nuevo argumento, tam (la talla del array ), puesto que esa información es necesaria. ® Debemos evitar dicho parámetro ya que los contenedores como vector o list ya contienen internamente su talla. Programación Avanzada - Tema 4: Programación Genérica – 40
  • 41. 4.2.1 Aritmética de punteros (II) ® Planteamos una segunda versión de find() para arrays en la que sustituimos el tamaño del array por un puntero que sirve como centinela que marca el final: template < class T> first last T ∗ f i n d ( T ∗ f i r s t , T ∗ l a s t , const T & v a l o r ) { if ( ! first || ! l a s t ) return 0 ; while ( f i r s t ! = l a s t ) 1 3 5 7 11 i f ( ∗ f i r s t = = v a l o r ) return f i r s t ; else ++ f i r s t ; array r e t u r n 0 ; / / NULL array+5 } last y array+5 apuntan al elemento siguiente del último, aunque no exista i n t main ( ) { int array [5]={1 ,3 ,5 ,7 ,11}; i f ( f i n d ( array , array +5 , 7) ) ... Programación Avanzada - Tema 4: Programación Genérica – 41
  • 42. 4.2.2 Iteradores ® Gracias a la aritmética de punteros hemos encontrado una buena estrategia para pasar los argumentos a find(): utilizamos la dirección del primer elemento del array y la del elemento siguiente al último. ® Para aplicar la misma estrategia a contenedores secuenciales, deberemos usar una abstracción del concepto de puntero: el iterador: µ Cada contenedor define el tipo: contenedor<tipobase>::iterator µ Cada contenedor define las funciones begin() y end() que devuelven iteradores que “apuntan” al primero y al siguiente del último elementos respectivamente. Programación Avanzada - Tema 4: Programación Genérica – 42
  • 43. 4.2.2 Iteradores (II) i n t main ( ) { vector <int > v i ; / / . . . rellenamos v i vi vector<int> i f ( f i n d ( v i . begin ( ) , v i . end ( ) , 7 ) ) // ... ... l i s t <int > l i ; vi.begin() vi.end() / / . . . rellenamos l i vector<int>::iterator vector<int>::iterator i f ( f i n d ( l i . begin ( ) , l i . end ( ) , 7 ) ) // ... li } list<int> ® Aquí usamos una nueva versión de li.begin() li.end() find() que utiliza iteradores en lugar list<int>::iterator list<int>::iterator de punteros. Implementaremos esta versión más adelante. Programación Avanzada - Tema 4: Programación Genérica – 43
  • 44. 4.2.2 Iteradores (III) ® Los iteradores son objetos que # include < iostream > soportan el mismo conjunto de # include < l i s t > operaciones que los punteros: ++, u s i g n namespace s t d ; *, == y != pero tienen una i n t main ( ) { implementación diferente para cada i n t primos [ 5 ] = { 1 , 3 , 5 , 7 , 1 1 } ; contenedor. l i s t < i n t > l i ( primos , primos +5) ; ® En el ejemplo de la derecha, l i s t < i n t > : : i t e r a t o r i t e r = l i . begin ( ) ; c o u t < < "{ " ; recorremos una lista usando un while ( i t e r ! = l i . end ( ) ) { iterador. cout < < ∗ i t e r < < " " ; ++ i t e r ; ® Observa que creamos el contendor li } usando un par de iteradores (en este c o u t < < "}" < < e n d l ; } caso, punteros) sobre un array. { 1 3 5 7 11 } Programación Avanzada - Tema 4: Programación Genérica – 44
  • 45. 4.2.2 Iteradores (IV) ® Volviendo a nuestra función find(), aquí tenemos la versión definitiva, válida para todo tipo de contenedor secuencial y cualquier array : hemos conseguido un algoritmo genérico usando iteradores como abstracción del concepto de puntero: template < class I t e r a d o r , class T> I t e r a d o r f i n d ( I t e r a d o r f i r s t , I t e r a d o r l a s t , const T & v a l o r ) { while ( f i r s t ! = l a s t ) { i f ( ∗ f i r s t == valor ) return f i r s t ; f i r s t ++; } return l a s t ; } ® Atención: Esta función find() es uno de los algoritmos genéricos de la biblioteca estándar. Programación Avanzada - Tema 4: Programación Genérica – 45
  • 46. 4.2.2 Iteradores (V) i n t main ( ) { $ g++ − o prueba prueba . cpp i n t primos [ 5 ] = { 1 , 3 , 5 , 7 , 1 1 } ; $ prueba int ∗ ptr ; Encontrado 7 p t r = f i n d ( primos , primos + 5 , 7 ) ; No encontrado b o n j o u r i f ( p t r ! = primos +5) c o u t < < "Encontrado 7" < < e n d l ; else c o u t < < "No encontrado 7" < < e n d l ; l i s t <string > ls ; l s . p u s h _ f r o n t ( "hola" ) ; l s . p u s h _ f r o n t ( "hello" ) ; l i s t <string >:: i te r at or it ; i t = f i n d ( l s . begin ( ) , l s . end ( ) , "bonjour" ) ; i f ( i t ! = l s . end ( ) ) cout <<"Encontrado bonjour"<< e n d l ; else cout <<"No encontrado bonjour"<< e n d l ; } Programación Avanzada - Tema 4: Programación Genérica – 46
  • 47. 4.2.3 Algoritmos genéricos en la biblioteca STL En la STL hay definidos más de 60 algoritmos genéricos: ® Algoritmos de búsqueda: find(), binary_search(), count(), search(), find_if(), count_if(), . . . ® Algoritmos de ordenación: sort(), partial_sort(), merge(), reverse(), rotate(), . . . ® Algoritmos de copiado, borrado y sustitución: copy(), remove(), remove_if(), replace(), swap(), . . . ® Algoritmos relacionales: equal(), includes(), mismatch(), . . . ® Algoritmos de generación y transformación: fill(), for_each(), generate(), transform(), . . . ® Algoritmos numéricos: accumulate(), partial_sum(), inner_product(), . . . ® Algoritmos sobre conjuntos: set_union(), set_difference(), . . . Programación Avanzada - Tema 4: Programación Genérica – 47
  • 48. 4.2.4 Ejemplos de utilización de algoritmos genéricos ® Para utilizar los algoritmos genéricos hay que incluir el fichero de cabecera correspondiente: #include <algorithm> ® Supongamos, por ejemplo, que necesitamos implementar la función is_elem() de modo que devuelva true si un número dado es un elemento de un vector ordenado de enteros. Podemos usar cuatro posibles algoritmos genéricos: 1. find() busca un valor en una colección no ordenada marcada con dos iteradores first y last. Si se encuentra el valor, devuelve un iterador al valor. En caso contrario, devuelve last. 2. binary_search() busca en una colección ordenada. Devuelve true o false si encuentra el valor o no. Es más eficiente que find(). 3. count() devuelve el número de repeticiones de un valor. 4. search() busca una subsecuencia en una secuencia. Devuelve un iterador al principio de la subsecuencia. Programación Avanzada - Tema 4: Programación Genérica – 48
  • 49. 4.2.4 Ejemplos de utilización de algoritmos genéricos (II) ® Una posible implementación de la función is_elem() puede usar binary_search(), ya que tenemos la garantía de que la secuencia de números está ordenada. # include < a l g o r i t h m > # include < v e c t o r > # include < iostream > using namespace s t d ; bool is_elem ( const v e c t o r < i n t > & v , i n t v a l o r ) { r e t u r n b i n a r y _ s e a r c h ( v . begin ( ) , v . end ( ) , v a l o r ) ; } i n t main ( ) { int array [8]={1 ,2 ,3 ,5 ,7 ,11 ,13 ,17}; v e c t o r < i n t > primos ( a r r a y , a r r a y +8) ; i f ( is_elem ( primos , 1 1 ) ) c o u t < < "11 esta en primos" ; else c o u t < < "11 no esta en primos" ; } Programación Avanzada - Tema 4: Programación Genérica – 49
  • 50. 4.2.4 Ejemplos de utilización de algoritmos genéricos (III) ® La función is_elem(), como hemos visto, supone que el vector de enteros que recibe está ordenado. Pero, ¿y si no está garantizado? ® Podemos escribir una nueva versión que hace una copia del contenedor, la ordena y sobre la versión ordenada realiza la búsqueda: # include < a l g o r i t h m > # include < v e c t o r > # include < iostream > using namespace s t d ; bool is_elem ( const v e c t o r < i n t > & v , i n t v a l o r ) { vector <int > copia ( v . size ( ) ) ; copy ( v . begin ( ) , v . end ( ) , c o p i a . begin ( ) ) ; s o r t ( c o p i a . begin ( ) , c o p i a . end ( ) ) ; r e t u r n b i n a r y _ s e a r c h ( c o p i a . begin ( ) , c o p i a . end ( ) , v a l o r ) ; } Programación Avanzada - Tema 4: Programación Genérica – 50
  • 51. Fin Copyright c 2010 José Luis Llopis Borrás Realizada con ujislides c 2002-3 Sergio Barrachina (barrachi@icc.uji.es)