The list class builds a doubly linked list of ListItem structures. Each
list item structure contains a link to the item ahead of it in the list
and the item behind it in the list. It also contains an item of type
MyType. This is the type of items that you want the list to store. Here is
a quick and simple example of using the list just to give you an idea:

#include "list.h"
#include <stdio.h>

void main(void)
{
    List<int> myList;

    myList.Add(4);        // adds items to the tail of the list
    myList.Add(5);
    myList.Add(99);
    myList.Add(-1);
    myList.AddHead(0);
    myList.Add(15);
    myList.Add(4);

    // list now looks like (head to tail left to right) 0 4 5 99 -1 15 4

    myList.Remove(5);    // remove all occurences of 5
    myList.Remove(4);    // remove all occurences of 4

    // list now looks like 0 5 99 -1 15

    myList.Count();
    printf("items in list = %d\n",myList.Count());    // prints 5

    // concatenate two lists (of the same type)
    List<int> listTwo;
    listTwo.Add(3);
    listTwo.Add(9);

    myList.Append(listTwo);    // adds listTwo to the tail of myList

    // copy one list into another
    listTwo = myList                // empties listTwo, then makes it
contain the same items as myList
                                            // NOTE: if this were a list of
pointers to allocated memory and you
                                            // walked both lists and
deallocated the memory you would
                                            // be deallocating it twice !!!!

    // myList and listTwo both now look like 0 5 99 -1 15 3 9

    printf("4th item is %d\n",myList[3]);    // prints -1

    myList.Release();    // remove all items from the list
}

That's the basic stuff covered, the rest comes next. To create a list of
some other type just swap int for that type name. The list does a binary
copy of anything you add to it and stores its own copy in the ListItem _data
field. You can walk the list yourself using pointers to ListItem
structures and the GetHead() and GetTail() methods. The Find() method will
find an item containing the value you give the method from a certain point
in the list onwards (towards tail).
Here's some examples:

#include "list.h"
#include <stdio.h>

enum { BANANA, LEMON, APPLE, PEAR, GRAPE, MELON, ORANGE, PEACH };

void main(void)
{
    List<Fruit> int;

    fruit.Add(BANANA);
    fruit.Add(PEACH);
    fruit.Add(LEMON);
    fruit.Add(MELON);
    fruit.Add(APPLE);
    fruit.Add(ORANGE);

    ListItem<Fruit> * pItem = fruit.GetHead();
    int pos = 0;

    while(pItem)
    {
        printf("Item %d = %d\n",pos++,pItem->Get());    // Get() method
retrieves data from the list item
        pItem = pItem->Next();
    }

    pItem = fruit.GetHead();
    fruit.InsertBefore(APPLE,pItem);
    fruit.InsertAfter(PEAR,pItem);

    // list now looks like APPLE, BANANA, PEAR, PEACH, LEMON, MELON,
APPLE,ORANGE

    pItem = fruit.GetTail();
    fruit.Remove(pItem);

    // ORANGE is not in the list anymore now
    pItem = fruit.Find(APPLE);                            // find first
occurence of  APPLE
    pItem = fruit.Find(APPLE,pItem);                // this would find the
same item again because the first
                                                                        //
item it finds is the one you give it!
    pItem = fruit.Find(APPLE,pItem->Next());    // find next occurence of
APPLE

    fruit.Release();
}

Three more topics to cover, using array brackets, structures and dynamic
allocation. Firstly arrays. You can say things like fruit[3] and this
returns to you the data at that position in the list (fourth item in this
example) which would be PEACH by the end of the previous example. It does
NOT return the pointer to the list item. If you say fruit[-1] you will get
back a random answer. You need to call fruit.SetErrorObject(-99) for example
to make the list return -99 to you when you specify an index out of bounds.
I deal with it this way because if I choose a value to mean an error has
occured it might be that in your data the error value is the same as valid
data and so you wouldn't be able to tell if an error had occured.

Here's an example of lists of structures:

#include "list.h"
#include <stdio.h>

enum Colour { RED, BLACK, BLUE, YELLOW, GREEN, BROWN, PINK, ORANGE };

struct Person
{
    int age;
    Colour colour;
};

void main(void)
{
    List<Person> people;
    Person edd;
    Person nick;
    Person bill;
    Person error;

    nick.age = 9;
    nick.height = 50;
    edd.age = 3;
    edd.height = 130;
    bill.age = 50;
    bill.height = 99;

    error.age = -1;
    error.height = -1;
    people.SetErrorObject(error);

    people.Add(edd);
    people.Add(nick);
    people.Add(bill);

    printf("Item 2 is %d years old and %d cm
tall\n",people[1].age,people[1].height);
    printf("Item -3 is %d years old and %d cm
tall\n",people[-3].age,people[-3].height);    // prints -1s

    people.Release();
}

B.T.W. The list destructor calls Release() if you don't remember to.

Lastly you can use the list to store pointers to things, handy for
dynamically allocated strings for example. The list does NOT deallocate
memory pointed to by the pointers it stores and it ONLY stores pointers, not
what they point at. Therefore if you modify a string after you have added
its address to the list you will be changing the item in the list. This is
not true for all the previous examples where pointers have not been used.

#include "list.h"
#include <string.h>

void main(void)
{
    List<char *> names;
    char * temp;

    temp = new char [5];
    strcpy(temp,"Bill");
    names.Add(temp);

    temp = new char [6];
    strcpy(temp,"Ximon");
    names.Add(temp);

    temp = new char [4];
    strcpy(temp,"Edd");
    names.Add(temp);

    // list now holds three addresses
    // names.Release();         // if you did this now you would lose the
addresses and would not be able
                                        // to deallocate the memory you
allocated !!!!

    // you must either walk the list and deallocate each string or use a for
loop and array brackets
    // both examples are given here, you would only do this once (not twice
like I have by giving two examples, this will fail if you run it !!!)

    for(int i = 0; i < people.Count(); i++)
    {
        delete[] people[i];
    }

    // OR
    ListItem<char *> pItem = names.GetHead();
    while(pItem)
    {
        delete[] pItem->Get();
        pItem = pItem->Next();
    }

    names.Release();
}

There you go :)
