Created: 2025-04-16 mié 00:33
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.
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;
}
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&)
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
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?
};
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: \(> .. >\).
// -- 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;
}
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
// 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;
}