Annak idején ott hagytuk abba, hogy elkészítettünk egy új adatszerkezetet, ami nem más volt mit a láncolt lista. Elkészítettünk hozzá egy használható felhasználói felületet, aztán elkezdhetünk gondolkozni azon, hogy mi van, ha mi nem egyszerű inteket akarunk abban a listában tárolni, hanem valami teljesen mást? Mert hát miért is akarnánk az inteknél leragadni, amikor annyi minden van még.

Ilyen megközelítésben természetesen eléggé használhatatlannak bizonyul a kódunk, mert csak arra az egy típusra működik megfelelően. Még szerencse, hogy itt vannak a sablonok, amikkel ez a probléma relatíve egyszerűen áthidalható.

Bevezetésnek kezdjünk valami egyszerű függvénnyel, mondjuk amivel össze tudunk adni mindenféle objektumot, aminek meg van adva a + operátora (tény, hogy a példa cseppet értelmetlen, de mondjuk gondoljunk akkor egy sort() függvényre, aminek végülis mindegy, hogy milyen adatokat hasonlít össze, amíg annak az adatszerkezetnek megvan a < vagy > operátora).

#include <string>
#include <iostream>

template<class T> T Add(const T& o1, const T& o2)
{
    return o1 + o2;
}

int main()
{
    int i1 = 1;
    int i2 = 2;
    int i3 = Add(i1, i2);
    std::cout << i3 << std::endl;

    std::string s1 = "abc";
    std::string s2 = "xyz";
    std::string s3 = Add(s1, s2);
    std::cout << s3 << std::endl;
}

Ennyi az egész, tulajdonképpen csak a választott függvényünk elé tesszük, hogy template<class T> és mindenhol, ahol egyébként mondjuk int-et írtunk volna, ott T-t használunk. Hasonló a helyzet az osztályok esetében is, nézzük először a már megírt cList.h fájlunk sablonosított változatát.

#ifndef __CLIST_H__
#define __CLIST_H__

template<class T> struct listNode
{
    listNode<T> *prev;
    listNode<T> *next;
    T data;

    listNode()
    {
        next = prev = this;
    }
    listNode(const T& num)
    {
        next = prev = this;
        data = num;
    }
};

template<class T> class cList
{
    private:
        listNode<T>* list;

        void Create(void);
        void Destroy(void);

        void Remove(listNode<T>* target);
        listNode<T>* Get(listNode<T>* target);
    public:
        cList(void);
        cList(const cList<T> &other);
        ~cList(void);

        class E_OUTOFMEMORY {};
        class E_INVALIDITEM {};

        void left_insert(listNode<T>* target, listNode<T>* data);
        void right_insert(listNode<T>* target, listNode<T>* data);
        listNode<T>* get_first(void);
        listNode<T>* get_last(void);
        listNode<T>* Search(const T& number);

        cList<T>& operator+(const cList<T>& other);
        cList<T>& operator=(const cList<T>& other);
        cList<T>& operator>>(T& number);
        cList<T>& operator>>(listNode<T>* pointer);
        cList<T>& operator<<(const T& number);
        cList<T>& operator<<(listNode<T>* pointer);
};

Pár <T>-től talán még megszabadulhatnánk, ha a listNode osztályt a cList osztályon belül adnánk meg, de most már mindegy (egy kis gyakorlás gyanánt mindenki megcsinálhatja magának). Most, hogy volt egy kis szünet, mindenki kifújta magát, visszatérhetünk az osztály metódusainak kibontogatására (maradunk a cList.h-ban):

template<class T> void cList<T>::Create(void)
{
    list = new listNode<T>();
    if (!list) throw E_OUTOFMEMORY();
 
    list->next = list;
    list->prev = list;
    return;
}

template<class T> void cList<T>::Destroy(void)
{
    while (list->prev != list) {
        Remove(list->prev);
    }
    return;
}

template<class T> void cList<T>::Remove(listNode<T>* target)
{
    listNode<T>* tmp = Get(target);
    delete tmp;
    return;
}

template<class T> listNode<T>* cList<T>::Get(listNode<T>* target)
{
    target->prev->next = target->next;
    target->next->prev = target->prev;
    target->prev = target;
    target->next = target;
    return target;
}

template<class T> cList<T>::cList(void)
{
    Create();
}

template<class T> cList<T>::cList(const cList<T>& other)
{
    Create();
 
    listNode<T>* tmp = other.list->next;
    while (tmp != other.list) {
        listNode<T>* node = new listNode<T>(tmp->data);
        if (!node) throw E_OUTOFMEMORY();
 
        this->left_insert(this->list, node);
        tmp = tmp->next;
    }
}

template<class T> cList<T>::~cList(void)
{
    Destroy();
    delete list;
}

template<class T> void cList<T>::left_insert(listNode<T>* target, listNode<T>* data)
{
    data->prev = target->prev;
    data->next = target;
    target->prev->next = data;
    target->prev = data;
    return;
}
template<class T> void cList<T>::right_insert(listNode<T>* target, listNode<T>* data)
{
    data->prev = target;
    data->next = target->next;
    target->next->prev = data;
    target->next = data;
    return;
}

template<class T> listNode<T>* cList<T>::get_first(void)
{
    if (list->next == list) throw E_INVALIDITEM();
    return Get(list->next);
}
template<class T> listNode<T>* cList<T>::get_last(void)
{
    if (list->prev == list) throw E_INVALIDITEM();
    return Get(list->prev);
}

template<class T> listNode<T>* cList<T>::Search(const T& number)
{
    listNode<T>* tmp = list->next;
    while (number != tmp->data && tmp != list) {
        tmp = tmp->next;
    }
    if (tmp == list) throw E_INVALIDITEM();
    return tmp;
}

template<class T> cList<T>& cList<T>::operator+(const cList<T>& other)
{
    listNode<T>* tmp = other.list->next;
    while (tmp != other.list) {
        listNode<T>* node = new listNode<T>(tmp->data);
        if (!node) throw E_OUTOFMEMORY();
 
        this->left_insert(this->list, node);
        tmp = tmp->next;
    }
}
template<class T> cList<T>& cList<T>::operator=(const cList<T>& other)
{
    if (this == &other) return *this;
    this->Destroy();
 
    listNode<T>* tmp = other.list->next;
    while (tmp != other.list) {
        listNode<T>* node = new listNode<T>(tmp->data);
        if (!node) throw E_OUTOFMEMORY();
 
        this->left_insert(this->list, node);
        tmp = tmp->next;
    }
    return *this;
}

template<class T> cList<T>& cList<T>::operator>>(T& number)
{
    number = get_last()->data;
    return *this;
}
template<class T> cList<T>& cList<T>::operator>>(listNode<T>* pointer)
{
    pointer = get_last();
    return *this;
}
 
template<class T> cList<T>& cList<T>::operator<<(const T& number)
{
    listNode<T>* tmp = new listNode<T>(number);
    if (!tmp) throw E_OUTOFMEMORY();
 
    left_insert(list, tmp);
    return *this;
}
template<class T> cList<T>& cList<T>::operator<<(listNode<T>* pointer)
{
    left_insert(list, pointer);
    return *this;
}

#endif

Nincs más hátra, mint egy megfelelő main.cpp-vel tesztelni ezt a rengeteg kódot, hogy egyáltalán működik-e ez az egész:

#include <string>
#include <iostream>

#include "cList.h"

int main()
{
        cList<int> list1;
        list1 << 1 << 2 << 3 << 4 << 5;
        int num;
        list1 >> num;
        std::cout << num << std::endl;

        cList<char> list2;
        list2 << 'a' << 'b' << 'c' << 'd' << 'e';
        char chr;
        list2 >> chr;
        std::cout << chr << std::endl;

        cList<std::string> list3;
        list3 << "alma" << "barack" << "citrom" << "dinnye" << "eper";
        std::string str;
        list3 >> str;
        std::cout << str << std::endl;
}

A fenti kód lefordítása majd futtatása után keletkezett kimenet legjobb esetben az, hogy 5, e, eper (mindegyik új sorban). Ezzel a sablonizációs bejegyzés végére is értünk, a legközelebbi alkalommal származtatni fogunk, ha a C++ istenei is így akarják.