Las plantillas en C++ son una característica poderosa que permite escribir código genérico y reutilizable. Las plantillas permiten definir funciones y clases que operan con cualquier tipo de datos, sin sacrificar la seguridad de tipos. Este tema cubre los conceptos básicos de las plantillas, cómo se utilizan y algunos ejemplos prácticos.

Conceptos Clave

  1. Plantillas de Función: Permiten definir funciones que pueden operar con diferentes tipos de datos.
  2. Plantillas de Clase: Permiten definir clases que pueden manejar datos de cualquier tipo.
  3. Parámetros de Plantilla: Los tipos de datos que se pasan a las plantillas.
  4. Especialización de Plantillas: Permite definir implementaciones específicas para ciertos tipos de datos.

Plantillas de Función

Definición

Una plantilla de función se define utilizando la palabra clave template seguida de la lista de parámetros de plantilla entre ángulos (< >). Aquí hay un ejemplo básico:

#include <iostream>
using namespace std;

template <typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

int main() {
    cout << "Max of 3 and 7: " << max(3, 7) << endl;
    cout << "Max of 3.5 and 2.1: " << max(3.5, 2.1) << endl;
    cout << "Max of 'a' and 'z': " << max('a', 'z') << endl;
    return 0;
}

Explicación

  • template <typename T>: Declara una plantilla con un parámetro de tipo T.
  • T max(T a, T b): Define una función max que toma dos argumentos del mismo tipo T y devuelve un valor del mismo tipo.
  • La función max se puede llamar con diferentes tipos de datos (int, double, char) sin necesidad de sobrecargar la función para cada tipo.

Ejercicio Práctico

Ejercicio: Escribe una plantilla de función llamada min que devuelva el menor de dos valores.

#include <iostream>
using namespace std;

template <typename T>
T min(T a, T b) {
    return (a < b) ? a : b;
}

int main() {
    cout << "Min of 3 and 7: " << min(3, 7) << endl;
    cout << "Min of 3.5 and 2.1: " << min(3.5, 2.1) << endl;
    cout << "Min of 'a' and 'z': " << min('a', 'z') << endl;
    return 0;
}

Plantillas de Clase

Definición

Las plantillas de clase permiten definir clases que pueden manejar datos de cualquier tipo. Aquí hay un ejemplo básico de una clase de plantilla para una pila (stack):

#include <iostream>
using namespace std;

template <typename T>
class Stack {
private:
    T* arr;
    int top;
    int capacity;

public:
    Stack(int size = 10);
    ~Stack();
    void push(T x);
    T pop();
    T peek();
    bool isEmpty();
    bool isFull();
};

template <typename T>
Stack<T>::Stack(int size) {
    arr = new T[size];
    capacity = size;
    top = -1;
}

template <typename T>
Stack<T>::~Stack() {
    delete[] arr;
}

template <typename T>
void Stack<T>::push(T x) {
    if (isFull()) {
        cout << "Stack Overflow\n";
        return;
    }
    arr[++top] = x;
}

template <typename T>
T Stack<T>::pop() {
    if (isEmpty()) {
        cout << "Stack Underflow\n";
        exit(EXIT_FAILURE);
    }
    return arr[top--];
}

template <typename T>
T Stack<T>::peek() {
    if (!isEmpty()) {
        return arr[top];
    } else {
        exit(EXIT_FAILURE);
    }
}

template <typename T>
bool Stack<T>::isEmpty() {
    return top == -1;
}

template <typename T>
bool Stack<T>::isFull() {
    return top == capacity - 1;
}

int main() {
    Stack<int> intStack(5);
    intStack.push(1);
    intStack.push(2);
    intStack.push(3);
    cout << "Top element is: " << intStack.peek() << endl;
    intStack.pop();
    cout << "Top element is: " << intStack.peek() << endl;

    Stack<string> stringStack(5);
    stringStack.push("Hello");
    stringStack.push("World");
    cout << "Top element is: " << stringStack.peek() << endl;
    stringStack.pop();
    cout << "Top element is: " << stringStack.peek() << endl;

    return 0;
}

Explicación

  • template <typename T>: Declara una plantilla con un parámetro de tipo T.
  • class Stack { ... }: Define una clase Stack que puede manejar datos de cualquier tipo T.
  • Los métodos de la clase Stack se definen fuera de la clase utilizando la sintaxis template <typename T> y Stack<T>::method.

Ejercicio Práctico

Ejercicio: Escribe una plantilla de clase llamada Queue que implemente una cola (queue) con las operaciones básicas enqueue, dequeue, front, isEmpty y isFull.

#include <iostream>
using namespace std;

template <typename T>
class Queue {
private:
    T* arr;
    int front;
    int rear;
    int capacity;
    int count;

public:
    Queue(int size = 10);
    ~Queue();
    void enqueue(T x);
    T dequeue();
    T frontElement();
    bool isEmpty();
    bool isFull();
};

template <typename T>
Queue<T>::Queue(int size) {
    arr = new T[size];
    capacity = size;
    front = 0;
    rear = -1;
    count = 0;
}

template <typename T>
Queue<T>::~Queue() {
    delete[] arr;
}

template <typename T>
void Queue<T>::enqueue(T x) {
    if (isFull()) {
        cout << "Queue Overflow\n";
        return;
    }
    rear = (rear + 1) % capacity;
    arr[rear] = x;
    count++;
}

template <typename T>
T Queue<T>::dequeue() {
    if (isEmpty()) {
        cout << "Queue Underflow\n";
        exit(EXIT_FAILURE);
    }
    T item = arr[front];
    front = (front + 1) % capacity;
    count--;
    return item;
}

template <typename T>
T Queue<T>::frontElement() {
    if (isEmpty()) {
        cout << "Queue is empty\n";
        exit(EXIT_FAILURE);
    }
    return arr[front];
}

template <typename T>
bool Queue<T>::isEmpty() {
    return count == 0;
}

template <typename T>
bool Queue<T>::isFull() {
    return count == capacity;
}

int main() {
    Queue<int> intQueue(5);
    intQueue.enqueue(1);
    intQueue.enqueue(2);
    intQueue.enqueue(3);
    cout << "Front element is: " << intQueue.frontElement() << endl;
    intQueue.dequeue();
    cout << "Front element is: " << intQueue.frontElement() << endl;

    Queue<string> stringQueue(5);
    stringQueue.enqueue("Hello");
    stringQueue.enqueue("World");
    cout << "Front element is: " << stringQueue.frontElement() << endl;
    stringQueue.dequeue();
    cout << "Front element is: " << stringQueue.frontElement() << endl;

    return 0;
}

Especialización de Plantillas

Definición

La especialización de plantillas permite definir implementaciones específicas para ciertos tipos de datos. Aquí hay un ejemplo de una especialización de plantilla para una función max que maneja cadenas de caracteres (const char*):

#include <iostream>
#include <cstring>
using namespace std;

template <typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

// Especialización para const char*
template <>
const char* max<const char*>(const char* a, const char* b) {
    return (strcmp(a, b) > 0) ? a : b;
}

int main() {
    cout << "Max of 3 and 7: " << max(3, 7) << endl;
    cout << "Max of 3.5 and 2.1: " << max(3.5, 2.1) << endl;
    cout << "Max of 'a' and 'z': " << max('a', 'z') << endl;
    cout << "Max of \"apple\" and \"banana\": " << max("apple", "banana") << endl;
    return 0;
}

Explicación

  • template <>: Indica una especialización de plantilla.
  • const char* max<const char*>(const char* a, const char* b): Define una implementación específica de la función max para el tipo const char*.

Conclusión

Las plantillas en C++ son una herramienta poderosa para escribir código genérico y reutilizable. Permiten definir funciones y clases que pueden operar con cualquier tipo de datos, lo que mejora la flexibilidad y la reutilización del código. En este tema, hemos cubierto los conceptos básicos de las plantillas de función y clase, así como la especialización de plantillas. Con estos conocimientos, estarás mejor preparado para escribir código más eficiente y adaptable.

En el próximo tema, exploraremos el manejo de excepciones en C++, una característica crucial para escribir programas robustos y manejables.

© Copyright 2024. Todos los derechos reservados