Of course, in C++ it is normal and sane to just use std::list :) Here are a few of the design things that are done differently by the std::list and normal implementations thereof:
- Templates are used to avoid having to cast from void*. This also implies that you can make a list to hold any type you like, but for any given list, all contents are of the same type. It also implies that code is basically generated for each type of list that actually gets used within the code. In well-designed C++ code, meaningful use of void* is exceedingly rare. It is basically a request to take all of the improvements that C++ makes over C's type system and throw them out the window.
- Everything is done in new, proper, "modern" style instead of working with ancient libraries and methods that only still exist for "bend-over-backwards compatibility" with C. So for example, the node struct is declared *without* the "typedef struct idiom" (which is meaningless in C++), nodes are allocated using new and deallocated using delete (which allows for constructors and destructors to be called - copying objects bitwise via memcpy() is not safe for objects that contain pointers or which are designed for polymorphism), and stuff is done to a list using member functions of the list object.
Also, while the headers in your project aren't needed for the list implementation really (except stdlib.h for malloc() - note that including malloc.h explicitly is redundant) but just for testing, you should be aware that:
- "iostream.h" is now properly spelled "iostream", and the necessary symbols should be imported from the std namespace.
- "stdlib.h", similarly, is now properly spelled "cstdlib", but you should hardly ever need it in good C++ code.
- "string.h" isn't actually used for anything you're doing, and furthermore has nothing to do with what good C++ developers call a "string". It is properly spelled "cstring" and provides access to strcpy() and all of that, but it is a better idea to use "string" - a modern header - and the C++ std::string object to represent text. It will save many, many headaches in the long run.
- The code is written in such a way that users do not need to know about the existance of list nodes. If you tell the list to "add" some value, it will create a node to store that value, and then append the node to the list. This is the concept of "encapsulation", and it is a very good thing. It prevents having to do lots of work all over the code, by doing it in the one place where it ought to be done.
The example code can be written as follows:
// No .h file of our own.#include <iostream>#include <list>#include <iterator>using namespace std;struct test { int i;};int main() { list<test> myList; // we don't need to write an initializer; we use the // default constructor here. test t; // Put a value into our test struct so that there is something valid to // print out. BTW, just in case you weren't aware - the 'i' in your loop // from before has nothing to do with the 'i' member of any given instance // of the test struct. t.i = 1; // Insert three copies of that struct into the list. for (int i = 0; i < 3; ++i) { myList.push_back(t); } // Iterate through the list and print each value. for (list<test>::iterator it = myList.begin(); it != myList.end(); ++it) { cout << "t.i = " << it->i; } // All the memory is automatically cleaned up at this point by the // destructor of the list object - something your code does not attempt.}