TEMA 8: GENERICIDAD.

Contenidos

1. Genericidad

  • La genericidad no es una propiedad esencial para que un lenguaje sea considerado OO.
  • Podríamos decir que la genericidad es otro tipo de polimorfismo.
  • Recordad lo que decíamos en las primeras prácticas cuando hablabamos de List y Stack:

    typedef char Element; // Hasta que veamos genericidad usaremos este typedef.
    

    Element era el tipo de los elementos almacenados en listas y pilas.

  • Estaría bien disponer de una característica del lenguaje que nos permitiera hacer esto no solo para un tipo, sino para todos los tipos que queramos.
  • De este modo podríamos crear simultáneamente listas y/o pilas de caracteres, de enteros, de droides, etc…

2. Genericidad y C++

  • C++ la incorpora mediante el concepto de plantilla (template) y nos permite aplicar la idea tanto a clases como a funciones.
  • Un template o plantilla es un mecanismo que permite al programador usar tipos como parámetros para una clase o una función.
  • Veamos un ejemplo muy sencillo aplicado a funciones, imaginad que queremos crear una función que intercambie dos datos del mismo tipo, p.e. enteros, cadenas o droides:

    // [1]                        // [2]
    void swap (int a , int b) { | void swap (string a , string b) {
      int t = a;                |   string t = a;
          a = b;                |          a = b;
          b = t;                |          b = t;
    }                           | }
    // [3]
    void swap (Droid a , Droid b) {
      Droid t = a;
            a = b;
            b = t;
    }
    
  • ¿Cómo son todas las funciones anteriores?
  • ¿Y si hubiera una forma de escribir el código una sola vez parametrizando el tipo de dato con el que trabajan?

2.1. Plantillas de funciones

  • La hay, mediante el uso de templates o plantillas de funciones, mira:

    // Así declaramos una plantilla de una función.
    // La función todavía no existe.
    template <class T>              // Podemos emplear typename en lugar
                                    // de class
    void swap (T a , T b) {
      T t = a;
        a = b;
        b = t;
    }
    
  • Para que la función exista hay que instanciar su plantilla, p.e.:

    int s, t;
    string m, n;
    ...
    swap (s, t);                    // swap (int, int)
    swap (m, n);                    // swap (string, string)
    
  • Para poder instanciar la plantilla de una función genérica tenemos que tener accesible el código fuente de su declaración.
  • Es por eso que el código de implementación de la plantilla de una función o una clase suele ir en ficheros de cabecera: '.h'

2.2. Plantillas de clases

  • De forma similar a como hemos hecho para funciones podemos hacerlo para clases.
  • Si hacemos genérica la clase Node empleada en Tree:

    template <typename T>
    class Node {               // El tipo de los elementos es T
      public:
        Node (T e);
        ...
      private:
        T _key;
        Tree lefts, rights;
    };
    
  • La instanciamos así:

    Node<char> nc;                  // nc variable de tipo Node de caracter
    Node<Droid> nd;                 // nd variable de tipo Node de Droid
    
  • ¿ Y si Tree también es genérica ?
  • Entonces la clase Node anterior quedaría así:

    template <typename T>
    class Node {               // El tipo de los elementos es T
      public:
        Node (T e);
        ...
      private:
        T _key;
        Tree<T> lefts, rights;      // ¿Declaración o instanciación?
    };
    

2.3. Parámetros de un template

  • La mayoría de lenguajes que tienen genericidad sólo permiten usar como argumentos de la plantilla tipos.
  • C++ permite usar otras cosas, p.e. enteros o cadenas.

    template <typename T, int N> struct Array {
      T elements[N];
      ...
    };
    Array<char,12> ca;              // Array de 12 caracteres
    Array<Droid, 512> da;           // Array de 512 droides
    
  • Se permiten valores por defecto:

    template <typename T = int, int N = 100> struct Array {
      T elements[N];
      ...
    };
    Array<> ca;                     // Array<int, 100>
    Array<Droid> da;                // Array<Droid, 100>
    Array<Array<char, 120>> aa;     // Matriz de 100x120 caracteres
    

    En C++ previo al estándar de 2011 los ángulos de cierre dobles (\(>>\)) de un template deben estar separados, no pueden ir juntos: \(> .. >\).

2.4. Un ejemplo más completo

// -- The C++ programming language, 4th. ed.
////////////////////////
// Contenido del '.h' //
////////////////////////
template<typename T>
struct Link {
  Link* pre;
  Link* suc;
  T val;
};

template<typename T>
class List {
  Link<T>* head;
public:
  List() : head(nullptr) { }
  List(const T& t) : head (new Link<T>{0,0,t} ) { }
  ...
  void print_all() const;
};
/////////////////////////
// Contenido del '.cc' //
/////////////////////////

// Implementación del método "List<T>::print_all() const"
template<typename T> void List<T>::print_all() const {
  for (Link<T>* p = head; p; p = p −> suc) //p depende de T
  cout << *p;
}

3. Programación genérica

  • Como podrás imaginar esta característica abre las puertas a un nuevo modo de crear programas.
  • Ya no solo debes pensar en el código que escribes, sino a qué dará lugar cuando se instancie. Requiere llevar bastante más cuidado.
  • A principios de la década de los 90 Alexander Stepanov comenzó a desarrollar lo que por aquel entonces se denominó la biblioteca STL y que hoy en día forma parte de la biblioteca estándar de C++.
  • Obligó a los fabricantes de compiladores a incorporar todo lo que demandaba este nuevo estilo de programación. En cierto modo, STL se convirtió en el código de testeo de muchos fabricantes de compiladores de C++.
  • La genericidad que proporciona C++ está años luz a la genericidad que podemos encontrar en otros lenguajes (C#, Java, etc…) donde básicamente consiste en un preprocesador algo más elaborado.

4. La biblioteca estándar de C++

  • El lenguaje C++ no es sólo el compilador.
  • Es también su biblioteca estándar, gran parte de la cual está, escrita usando genericidad (STL).
  • En ella nos encontramos, junto a otros componentes, con una serie de clases que implementan TADS que representan contenedores (listas, pilas, colas, etc…).
  • Para usar cualquier componente de esta biblioteca debemos incluir un archivo de cabecera que contiene el código que lo implementa. A continuación (si es un elemento genérico) debemos proceder a instanciarlo, p.e:

    #include <list>
    #include <queue>
    
    std::list<int> il;              // Lista de enteros
    std::list<Droid> dl;            // Lista de droides
    std::list<std::queue<char>> ql; // Lista de colas de caracteres
    std::queue<std::list<char>> lq; // Cola de listas de caracteres
    
  • Dispones de más información sobre esta biblioteca aquí.

4.1. Ejemplo de uso de std::vector

// Compilar con std=c++11
#include <iostream>
#include <vector>

class Robot {
 public:
  static int rn;
  const static Robot zero;

  Robot(std::string n, float bl = 1.0) {
    theName = n;
    theBattLevel = bl;
    rn++;
  }

  friend std::ostream& operator<< (std::ostream& os, const Robot& r);

  virtual ~Robot() {}

private:
  std::string theName;
  float theBattLevel;
};
std::ostream&
operator<< (std::ostream& os, const Robot& r) {
  os << "-----------------\n";
  os << "    Name: " << r.theName << '\n';
  os << "    BL: " << r.theBattLevel<< '\n';
  os << "-----------------\n";

  return os;
}

int Robot::rn = 0;
const Robot Robot::zero = Robot ("", 0.0);

int main(int argc, char *argv[])
{
  std::vector<Robot*> rv;

  for (int i = 0; i < 10; i++)
    rv.push_back (new Robot ("robot-" + std::to_string(Robot::rn),
                             static_cast<float>(.1*i)));


  std::cout << "\nBuilt robots: " << Robot::rn << "\n\n";

  for (int i = 0; i < 10; i++)
    std::cout << *rv[i] << '\n'; // ¿porqué *rv[i] y no rv[i]?

  return 0;
}

5. Aclaraciones

  • En ningún caso estas transparencias son la bibliografía de la asignatura, por lo tanto debes estudiar, aclarar y ampliar los conceptos que en ellas encuentres empleando los enlaces web y bibliografía recomendada que puedes consultar en la página web de la ficha de la asignatura y en la web propia de la asignatura.

Created: 2024-01-01 lun 17:45

Validate