Note that after freeing a pointer I generally set it to 0. That's also a very good habit to get into. If the element can't be created then the node itself is freed up. AddElement behaves like a constructor. It either fully succeeds in allocating everything or it fails and leaves allocated memory in a consistent state. The code relies on every node having a valid dataElement. You don't have to do this but I reckon it keeps the functions that use the list a bit simpler- there's no need to check if data is valid.
Only if the node etc is successfully allocated is the node added on the end of the list. In the test code (in the main function), every node added is checked to see that it worked. If any fail, no more are added as the only reason it could fail is running out of memory.
As each node is added, its associated element gets the auto incremented id and a string key assigned a "Null ID" value.
The m_head member variable is set when the first node is allocated and never changes after that.
In the main() function, a loop adds each element and sets the ID of each element to "KEYID:" plus index value. This uses a stringstream variable, just like the cout operator except the text is buffered in a string like variable.
On the next page : Example 1 Continued.

