Reputation: 1976
Bit of an odd one, I successfully implemented my own templated linked list as an educational exercise a while back. Now I know it works, I've been using it to create a list of nodes that hold "Sprite" objects, and at the time I understood it but I went and looked back at it today and I'm really confused.
If you'd be kind enough to look at my implementation and help me figure out.
How does the program KNOW that _data (in Node) should be assigned an object of NodeType?? (if you'll excuse the poor phrasing) I mean I have that function SetData
but I never even use it. And when I PushBack on the list for example, I just create a new Node of ListType, but I never set that node's data. The node constructor doesn't say _data = new NodeType
or anything.
And yet I can call GetData()
on any node in my list and get a Sprite object back. I mean I know it works, but I've forgotten how! Could someone take a look and remind me? It'd be much appreciated!
Node.hpp:
#if !defined NODE_HPP
#define NODE_HPP
template<typename NodeType>
class Node{
public:
Node( Node* prev = 0, Node* next = 0);
void SetData(NodeType newData);
NodeType* GetData();
Node* GetPrev();
Node* GetNext();
private:
template<typename ListType>
friend class List;
NodeType _data;
Node* _prev;
Node* _next;
};
///implement Node
template <typename NodeType>
Node<NodeType>::Node(Node* prev, Node* next) : _prev(prev), _next(next)
{}
template <typename NodeType>
void Node<NodeType>::SetData(NodeType newData)
{
_data = newData;
}
template <typename NodeType>
NodeType* Node<NodeType>::GetData()
{
return &_data;
}
template <typename NodeType>
Node<NodeType>* Node<NodeType>::GetPrev()
{
return _prev;
}
template <typename NodeType>
Node<NodeType>* Node<NodeType>::GetNext()
{
return _next;
}
#endif //define
List.hpp
#if !defined LIST_HPP
#define LIST_HPP
#include "Node.hpp"
///since we're creating a template everything must be defined in the hpp
template <typename ListType>
class List
{
public:
List();
bool Empty();
void PushFront();
void PushBack();
void PopBack();
void ClearList();
Node<ListType>* GetHead();
Node<ListType>* GetTail();
int NumberOfNodes();
private:
Node<ListType>* _head;
Node<ListType>* _tail;
int _size;
};
///implement List class here
template <typename ListType>
List<ListType>::List() : _head(0), _tail(0), _size(0)
{
}
template <typename ListType>
bool List<ListType>::Empty()
{
return _size == 0;
}
template <typename ListType>
void List<ListType>::PushFront()
{
_head = new Node<ListType>( _head , 0 );
if(Empty()) //this is the start of a new list, need to set _tail as well
_tail = _head;
else
_head->_prev->_next = _head; //set previous nodes _next to new _head
++_size;
}
template <typename ListType>
void List<ListType>::PushBack()
{
_tail = new Node<ListType>( 0 , _tail);
if(Empty()) //this is the start of a new list, need to set _head as well
_head = _tail;
else
_tail->_next->_prev = _tail; // set old tails _prev to new tail
++_size;
}
template <typename ListType>
void List<ListType>::PopBack()
{
if(!Empty()){
if(_tail != _head)
{
_tail = _tail->_next;
delete _tail->_prev; //delete memory at old tail
_tail->_prev = 0; //reassign new tails _prev pointer to null
}
else // We are deleting the last node so treatment is a little different.
{
delete _tail;
_tail = 0; _head = 0;
}
--_size;
}
}
template <typename ListType>
void List<ListType>::ClearList()
{
while(!Empty())
PopBack();
}
template <typename ListType>
Node<ListType>* List<ListType>::GetHead()
{
return _head;
}
template <typename ListType>
Node<ListType>* List<ListType>::GetTail()
{
return _tail;
}
template <typename ListType>
int List<ListType>::NumberOfNodes()
{
return _size;
}
#endif //define
Upvotes: 0
Views: 250
Reputation: 227468
The constructor doesn't explicitly do anything about _data
, so it default initializes it*. In other words, after construction, _data
is a default constructed NodeType
. It NodeType
were not default constructible, the template specialization wouldn't compile. The program "knows" to do this initialization because hopefully your compiler follows the rules set out in the C++ standard.
As for knowing the type, the compiler essentially replaces NodeType
for which ever type you instantiate the template with and creates a new type using this replacement. So Node<std::string> would result in a type where
_datais of type
std::string`.
NodeType
were a promitive type, then there would be no initialzation, and _data
would hold any old garbage value.Upvotes: 1