How to use templates in a c++ linked list? - c++

I don't fully understand the concept of templates and am trying to get some help on how to implement one on my linked list below. I'm trying to get my code to be able to support the following types : List< List<std::string> > List<std::string> List<int>. I was wondering if there was any way someone could give me an example of how to convert these items into templates in addition to trying to explain what is happening? I'm new to c++ so any help I can get would be appreciated.
#include <string>
#include <iostream>
#include <cstddef>
using Item = std::string;
// TURN DList into a template!
class DList {
private:
class DListNode {
public:
Item item;
DListNode * next;
DListNode * prev;
DListNode(Item i, DListNode *n=nullptr, DListNode *p=nullptr) {
item = i;
next = n;
prev = p;
}
};
DListNode * head;
DListNode * tail;
public:
class iterator {
DListNode *node;
public:
iterator(DListNode *n = nullptr) {
node = n;
}
Item& getItem() { return node->item; }
void next() { node = node->next; }
void prev() { node = node->prev; }
bool end() { return node==nullptr; }
friend class DList;
};
public:
DList() {
// list is empty
head = nullptr;
tail = nullptr;
}
bool empty() {
return head==nullptr;
}
void append(Item a) {
DListNode *node = new DListNode(a,nullptr,tail);
if ( head == nullptr ) {
// empty list
head = node;
tail = node;
} else {
tail->next = node;
tail = node;
}
}
void insertAfter(iterator it, Item item)
{
if(head == nullptr || it.node == nullptr) { // NULL iterator means insert at head
DListNode *node = new DListNode(item,head); // next=head, prev=NULL
if ( head == nullptr) // same as zyBook
head = tail = node;
else { // if inserting before head, it.node==NULL
head->prev = node;
head = node;
}
} else if (it.node == tail) {
DListNode *node = new DListNode(item,nullptr,tail); // next=NULL, prev=old tail
tail->next = node;
tail = node;
} else {
DListNode *node = new DListNode(item,it.node->next,it.node);
it.node->next = node;
node->next->prev = node;
}
}
void erase (iterator it) {
DListNode *succ = it.node->next; // successor node
DListNode *pred = it.node->prev; // predecessor node
if (succ != NULL)
succ->prev = pred;
if (pred != NULL)
pred->next = succ;
if (it.node == head)
head = succ; // head is following node
if (it.node == tail)
tail = pred; // tail is previous node
delete it.node; // delete the node; not shown in zyBook, but necessary in C/C++
// iterator is now invalid, caller should not use it again
}
iterator begin() {
return iterator(head);
}
iterator reverse_begin() {
return iterator(tail);
}
};
template <typename Item>
std::ostream& operator << (std::ostream& out, DList<Item> &l)
{
out << "{";
auto it = l.begin();
out << it.getItem();
it.next();
for(; !it.end(); it.next())
{
out << ", " << it.getItem();
}
out << "}" << std::endl;
return out;
}
int main()
{
{
DList<std::string> l;
l.append("eggs");
l.append("milk");
l.append("bread");
std::cout << l;
}
{
DList<int> l;
l.append(1);
l.append(2);
l.append(3);
std::cout << l;
}
return 0;
}

Actually, you almost have all you need, but you are still using a regualar class with a concrete type.
using Item = std::string;
class DList { ... };
So first we drop the concrete type:
// using Item = std::string;
class DList { ... }; // sure Item is now undefined...
Then we tell the class to be a template
template <typename Item>
class DList { ... };
Now Item got re-introduced, but instead of being a concrete type, it's now a generic one. That's it, you have a template list (assuming the list is implemented correctly, I didn't check).
Whenever you now instantiate your list:
DList<int>;
DList<std::string>;
// ...
You create a totally new, independent data type (which means especially, that you cannot assign a DList<int> to a pointer to DList<double>, just all alike as you cannot assign a int to a pointer to double either).
When you instantiate a template, every occurence of a template parameter will be replaced with the type you instantiated the template with, e. g. in DList<int>, every occurence of Item will be replaced with int.
Well, all this is just a very short introduction, there's quite a lot to follow yet, but that's rather to be handled in book than in an answer on stackoverflow...
Some notes to your node's constructor, though:
DListNode(Item i /* , ... */) { item = i; }
At very first, you should get used to using constructor's initialiser list (not to be confused with std::initializer_list):
DListNode(Item i /* , ... */) : item(i) { }
You avoid default initiasation + assignment in favour of direct initialisation by value. Additionally, some types (non-default constructible ones, const members and references) only can be initialised that way.
Then you are producing an unnecessary copy:
DListNode(Item i /* , ... */) : item(i) { }
// ^ temporary copy ^ final copy, created from temporary
You avoid that copy, if you accept the item by reference:
DListNode(Item const& i /* , ... */) : item(i) { }
// now copies from reference, one copy less
You can additionally provide move semantics:
DListNode(Item&& i /* , ... */) : item(std::move(i)) { }
so that objects you don't need outside the list any more can be moved into (well, actually their contents). In some cases, this can be much cheaper than a full copy...
All said about the constructor (apart from the initialiser list) applies to the append and insertAfter functions as well.
Initialiser lists and avoiding copies is general advice, unrelated to templates...

Related

C++ linked list Segmentation fault and valgrind errors

i need to code a linked list for university in c++, mostly to practice coding iterators.
I tested it with some basic cases and it works but after i pass it in valgrind and the test server for the program i get a list of different errors. Maybe somebody can help me not to despair.
(At the end i will append the error list)
template <typename T = float>
class ForwardList
{
struct Node
{
/// Constructs a Node from a data value and a link to the next element.
Node(const T &data, Node *next) : data{data}, next{next} {}
/// A Node owns all nodes after it, so it deletes them on destruction
~Node() { delete next; }
//Performs a deep copy of the Node and all Nodes after it. Bad practice but we got it like that
Node *clone() const
{
if (next == nullptr)
{
return new Node{data, nullptr};
}
else
{
return new Node{data, next->clone()};
}
}
T data;
Node *next;
};
public:
ForwardList() : head(nullptr) {}
/// Copy constructor performs a deep copy of the other list's Nodes
ForwardList(const ForwardList &other)
{
head = other.head->clone();
}
/// Destructor makes sure that all Nodes are correctly destroyed
~ForwardList()
{
while (head->next != nullptr)
{
Node *tmp = head;
head = head->next;
delete tmp;
}
delete head;
}
/// Copy assignment operator uses the copy-and-swap idiom to make a safe
/// assignment
ForwardList &operator=(ForwardList other)
{
swap(*this, other);
return *this;
}
/// Add an element to the front of the list.
void push_front(const T &value)
{
std::cout << "Num: " << numberOfNodes << std::endl;
Node *item = new Node(value, nullptr);
if (head==nullptr)
{
head = item;
}else
{
item->next=head;
head = item;
}
numberOfNodes++;
}
/// Remove the first element of the list. Calling this function on an empty
/// list is undefined behavior. When implementing this function, be careful
/// to delete the one and only the one element that is removed.
void pop_front()
{
Node *item;
item = head->next;
delete head;
head = item;
numberOfNodes--;
}
/// Get a reference to the first element of the list
/// (const and non-const version)
T &front()
{
return head->data;
}
const T &front() const
{
return head->data;
}
/// Return true is the list is empty
bool empty() const
{
return numberOfNodes == 0 ? true : false;
}
std::size_t size() const
{
return numberOfNodes;
}
friend void swap(ForwardList &l, ForwardList &r)
{
Node *tmp = l.head;
l.head = r.head;
r.head = tmp;
}
private:
Node *head;
size_t numberOfNodes = 0;
};
And now the fun part (i will put it on pastebin because its pretty long):
https://pastebin.com/4JAKkJtP
Your issue is that ~Node tries to delete its next, and you also try to walk the list in ~ForwardList. By deleting ~Node(), you let ForwardList handle cleanup and everything works.
The clue here is that valgrind reported use after free, meaning something was deleting a pointer twice. That was a clue to look at everything that deletes a Node* (or really, delete in general).

getting segmentation fault on this code for deletion of node in linked list

I'm trying to write a function for the deletion of a node in a linked list , given a double pointer to the head and a pointer to the node to be deleted. (the node to be deleted will not be the tail node)
this is what I have tried:-
public:
int value;
Node* next;
};
/*
headPtr is a reference to the head node(i.e. pointer to pointer) and
deleteNodePtr is the node which is to be deleted. You can see the Node definition above.
It is guaranteed that deleteNodePtr will not point to the last element.
*/
void deleteNode(Node** headPtr, Node* deleteNodePtr) {
Node* current;
current=*headPtr;
if (*headPtr==deleteNodePtr){
*headPtr=deleteNodePtr->next;
//delete current;
return;
}
else {
Node* prev = current;
while(current->next!=deleteNodePtr){
prev = current;
current=current->next;
}
prev->next =current->next;
//delete current;
return;
}
return;
}
I can see multiple things:
1 - In your while condition you are not checking that current is valid:
while(current->next!=deleteNodePtr){
prev = current;
current=current->next;
}
2- What probably is happening to you now: the deleteNodePtr points to the second element in the list, so you never enter to the while loop which means the prev == current which means that you are assigning current->next = current->next.
A solution for this would be:
void deleteNode(Node** headPtr, Node* deleteNodePtr) {
Node* current;
current = *headPtr;
if (current == deleteNodePtr) {
current = deleteNodePtr->next;
delete current;
}
else {
Node* prev = current;
current = current->next;
while (current!= nullptr && current != deleteNodePtr) {
prev = current;
current = current->next;
}
if(current!=nullptr)
{
prev->next = current->next;
delete current;
}
}
return;
}
3- You never checks if headPtr is valid, it couldn't be.
4- It's more a suggestion in the stylish thing rather than a code problem, so if you don't share this point of view, you can freely ignore it. At the very begin you assign *headPtr to current, but instead using current for further usage, you use headPtr. It would be much clear instead, if you always use current:
Original:
Node* current;
current=*headPtr;
if (*headPtr==deleteNodePtr){
*headPtr=deleteNodePtr->next;
//delete current;
return;
}
Suggestion:
Node* current;
current=*headPtr;
if (current==deleteNodePtr){
current=deleteNodePtr->next;
//delete current;
return;
}
This appears at first blush to be a C-style linked list written in C++. It's hard to say for sure since we don't have a bigger picture of your code. A C++ linked list would at least put these functions in a class, and not have a global Node.
The node is what's called an implementation detail. It helps us write the list, but users of the list class should not be able to declare their own nodes, nor should they be aware that nodes exist. Hopefully people using your class aren't calling this delete function explicitly.
Users of your List class might/should (depends) prefer to have iterators available to them. If anything writing iterators for your containers will allow them to be used with range-based for loops. Since your linked list appears to be singly linked, your iterators can only move forward. I also wrote my class with an iterator as I imagine it will help prevent copy/paste homework submissions in most cases.
Now to your function. Per my comment, passing the head as a double pointer makes no sense. Not once do you use it as a double pointer; you always de-reference it. So cut out the middle-man and pass it a node pointer from the get-go.
There's a case that's guaranteed not to happen, and that's that deleteNodePtr will never be the last node. That sounds bad. People can want to delete the last node, but they're allowed and no justification is given.
There's no code to catch an attempt to delete on an empty list.
Your specific issue seems to lie here:
while(current->next!=deleteNodePtr){
prev = current;
current=current->next;
}
You break out of the loop when current->next is the node to be deleted, and not current. You end up deleting the wrong node (current, which is 1 before deleteNodePtr), and that's likely going to cause issues down the line. For instance, if the second node is supposed to be deleted, you end up deleting your head, and that breaks stuff. I imagine that removing the ->next from your Boolean condition would fix the issue. I can't provide a deeper resolution without seeing more of your code.
=== Optional reading ===
Here's an extremely stripped down linked list written as a C++ class. You can run this code here: https://godbolt.org/z/7jnvje
Or compile it yourself.
#include <iostream>
// A simple linked list for integers
class List {
public:
List() = default;
~List();
// List(const List& other); // Copy ctor needed for Rule of 5
// List(List&& other) noexcept; // Move ctor needed for Rule of 5
void push_back(int val);
class iterator;
iterator begin();
iterator end();
iterator find(int val);
void erase(iterator it);
void clear();
// friend void swap(List& lhs, List& rhs); // Not Rule of 5; aids Rule of 5
// List& operator=(List other); // Assignment operator needed for Rule of 5
/*
* Quick note on Rule of 5 functions.
* If your class deals with heap-allocated resources, certain functions become
* required. The only one I included was the destructor. The signature of my
* assignment operator is different than you might see, but the reason is
* it's written to take advantage of the copy/swap idiom.
* https://stackoverflow.com/a/3279550/6119582
*/
private:
// Data
struct Node {
int value = 0;
Node *next = nullptr;
Node(int val) : value(val) {}
};
Node *m_head = nullptr;
Node *m_tail = nullptr;
// Functions
Node *find_node(int val);
};
class List::iterator {
public:
iterator(Node *loc) : location(loc) {}
iterator &operator++();
int operator*();
bool operator==(const List::iterator &rhs);
bool operator!=(const List::iterator &rhs);
private:
Node *location;
};
// List Implementation
List::~List() { clear(); }
void List::push_back(int val) {
if (m_tail) {
m_tail->next = new Node(val);
m_tail = m_tail->next;
return;
}
m_head = new Node(val);
m_tail = m_head;
return;
}
List::iterator List::begin() { return iterator(m_head); }
List::iterator List::end() { return iterator(nullptr); }
List::iterator List::find(int val) { return iterator(find_node(val)); }
void List::erase(iterator it) {
// Emtpy list or end()
if (!m_head || it == end())
return;
Node *toDelete = find_node(*it);
// Deleting head
if (toDelete == m_head) {
m_head = m_head->next;
delete toDelete;
return;
}
// Deleting tail
if (toDelete == m_tail) {
Node *walker = m_head;
while (walker->next != m_tail) {
walker = walker->next;
}
m_tail = walker;
delete m_tail->next;
m_tail->next = nullptr;
return;
}
// Delete any middle node; by moving value until it is the tail, then
// deleting the tail
while (toDelete->next) {
toDelete->value = toDelete->next->value;
if (toDelete->next == m_tail) {
m_tail = toDelete;
}
toDelete = toDelete->next;
}
delete toDelete;
m_tail->next = nullptr;
}
void List::clear() {
while (m_head) {
Node *tmp = m_head;
m_head = m_head->next;
delete tmp;
}
m_tail = nullptr;
}
List::Node *List::find_node(int val) {
if (!m_head) {
return nullptr;
}
Node *walker = m_head;
while (walker && walker->value != val) {
walker = walker->next;
}
return walker;
}
// List iterator implementation
List::iterator &List::iterator::operator++() {
location = location->next;
return *this;
}
int List::iterator::operator*() { return location->value; }
bool List::iterator::operator==(const List::iterator &rhs) {
return location == rhs.location;
}
bool List::iterator::operator!=(const List::iterator &rhs) {
return !(*this == rhs);
}
// Free function
// NOTE: Should take list by const reference, but I didn't add the necessary
// code for that. I'm not passing by value because I also left out Rule of 5
// code that is otherwise required.
// NOTE 2: Could also be templatized and made more generic to print any
// container, but that's outside the scope of this answer.
void print(List &list) {
for (auto i : list) {
std::cout << i << ' ';
}
std::cout << '\n';
}
int main() {
List list;
for (int i = 1; i <= 10; ++i) {
list.push_back(i);
}
print(list);
list.erase(list.find(1));
print(list);
list.erase(list.find(10));
print(list);
list.erase(list.find(6));
print(list);
auto it = list.begin();
for (int i = 0; i < 3; ++i) {
++it;
}
list.erase(it);
print(list);
list.erase(list.find(25)); // Bogus value; could throw if so desired
print(list);
}
Erasing is made much easier with a doubly-linked list, but we don't have one. My erase function makes some checks and handles the head and tail situations individually. For any node in the middle of the list, I don't bother deleting that node specifically. What I do instead is shuffle the value to be deleted to the tail of the list, and then delete the tail.
My comments indicate some things that were left out. I also didn't mark any functions as const. My iterator does not satisfy all requirements of a ForwardIterator. People can probably find other things I left out. I have a couple reasons for this. Mainly that this is quick and dirty code, and I prefer to not provide the temptation of a copy/paste solution.
It would be nice if all C++ instructors would actually teach C++, though. This form of linked list should not be taught in a C++ class anymore.

Iterator in my implementation of std::list does not work

I'm trying to write a template of list like std::list one.
This is my code in List.h:
#include <memory>
#include <cassert>
#include <iterator>
template<typename T, class Node>
class iterator : public std::iterator<std::bidirectional_iterator_tag, Node *, Node &> {
Node *underlying;
public:
explicit iterator(Node *n) : underlying(n) { };
iterator() : underlying(nullptr) { };
iterator &operator++() { //preinc
assert(underlying != nullptr && "Out-of-bounds iterator increment!");
underlying = underlying->next;
return *this;
}
iterator operator++(int) { //postinc
assert(underlying != nullptr && "Out-of-bounds iterator increment!");
iterator temp(*this);
++(*this);
return temp;
}
iterator &operator--() { //predec
assert(underlying != nullptr && "Out-of-bounds iterator decrement!");
underlying = underlying->previous;
return *this;
}
iterator operator--(int) { //postdec
assert(underlying != nullptr && "Out-of-bounds iterator decrement!");
iterator temp(*this);
--(*this);
return temp;
}
bool operator==(const iterator &rhs) {
return underlying == rhs.underlying;
}
bool operator!=(const iterator &rhs) {
return underlying != rhs.underlying;
}
T &operator*() {
return underlying->data;
}
};
template<typename T>
class List {
class Node {
public:
T data;
Node *previous;
Node *next; //is that T needed?
Node(T &d) : data(d) { };
};
private:
Node *head; //first element
Node *tail;
void create() { head = tail = NULL; }
void create(const List &rhs) {
iterator this_iter = head;
iterator rhs_iter = rhs.head;
while (rhs_iter != NULL) {
this_iter->data = (rhs_iter++)->data;
++this_iter;
}
};
public:
typedef T *iterator;
typedef const T *const_iterator;
typedef size_t size_type;
typedef T value_type;
List() { create(); };
List &operator=(const List &rhs) {
if (&rhs != this) {
create(rhs);
}
return *this;
};
List(const List &rhs) { create(rhs); };
~List() { while(head) remove(head); };
T *begin() { return head; };
T *end() { return tail; };
T front() { return head->data; };
T back() { return tail->data; };
bool empty() { return head == NULL; }
size_type size() {
size_t i = 0;
Node *node = head;
while (node) {
node = node->next;
i++;
}
return i;
};
T &operator[](size_type i) {
if (i < size() && i >= 0) {
Node *temp = head;
while (i > 0) {
temp = temp->next;
i--;
}
return temp->data;
}
throw std::out_of_range("Index out of range");
};
// const T &operator[](size_type i) const; //how to implement and do not duplicate code?
Node *push_back(value_type data) {
Node *n = new Node(data);
if (head == NULL) {
head = tail = n;
} else {
n->previous = tail;
tail->next = n;
tail = n;
}
return n;
};
Node *push_front(value_type data) {
Node *n = new Node(data);
if (head == NULL) {
head = tail = n;
} else {
n->next = head;
head->previous = n;
head = n;
}
return n;
};
void pop_front() {
remove(head);
};
void pop_back() {
remove(tail);
};
void remove(Node *n){
if(n == NULL) return;
if(n == head){
head = n->next;
head->previous =NULL;
}
else if(n == tail){
tail = n->previous;
tail->next = NULL;
}
else{
n->previous->next = n->next;
n->next->previous = n->previous;
}
delete n;
}
};
And this is main.cpp
#include <iostream>
#include "List.h"
int main(){
List<int> l;
l.push_back(1);
l.push_back(2);
l.push_back(3);
l.pop_back();
l.pop_front();
l.push_back(4);
l.push_back(5);
for (size_t i = 0; i < l.size(); i++)
std::cout << l[i] << "\n";
std::cout<<"Front "<<l.front();
std::cout<<"Back "<<l.back();
}
Actually push_back/front , pop_back/front and []operator work fine. But I get "Process finished with exit code 139"
error when I try use front() or back(). And I know that this iterator of list template does not work but I do know how to combine it up. Could anyone hint or help?
EDIT:
Ok, I have fixed the problem with removing and front(), tail() methods. But still the iterator thing doesnt work.
For example this code:
for(List<int>::iterator it = l.begin(); it!=l.end(); it++){
std::cout << it << "\n";
}
Gives me erros:
error: cannot convert ‘List<int>::Node*’ to ‘List<int>::iterator {aka int*}’ in initialization
for(List<int>::iterator it = l.begin(); it!=l.end(); it++){
^
error: comparison between distinct pointer types ‘List<int>::iterator {aka int*}’ and ‘List<int>::Node*’ lacks a cast [-fpermissive]
for(List<int>::iterator it = l.begin(); it!=l.end(); it++){
^
I know that te problem is with wraping the node with iterator template and that I have got "typename T *iterator".
Your begin and end methods return Node*, not your iterator type. And you made the iterator constructor that accepts Node* as an argument explicit; you told the compiler that implicit conversion from List<int>::Node* to List<int>::iterator is disallowed.
You must do one of:
Remove explicit from explicit iterator(Node *n) : underlying(n) { }; (though this risks implicit conversions in scenarios you don't want)
Change begin and end to return iterator(head) and iterator(tail) (performing explicit conversion to iterator) rather than head and tail (and change the return type of begin and end to iterator, which you should really do regardless)
You have some other issues too:
You should not have done typedef T* iterator in List; that hid the definition of your iterator class, so List never used it. Fixing that (and adding the necessary templating to your uses of iterator) makes it compile
The inheritance definition of iterator should be template<typename T, class Node> class iterator : public std::iterator<std::bidirectional_iterator_tag, T> not template<typename T, class Node> class iterator : public std::iterator<std::bidirectional_iterator_tag, Node *, Node &>; the latter is declaring the value from dereferencing should be Node * (when you want it to be T, the value in each Node)
end should return iterator<T, Node>(nullptr) not iterator(tail); otherwise, you stop before printing the value of tail, when you want to print tail before you terminate the loop.
Once all that is done, you should compile and get the results you expect. The code still has problems, e.g.
It's not const correct and offers no const versions of various accessors, which can prevent optimizations; you may end up recomputing size on every loop if the compiler can't figure out that the loop is actually non-mutating
The copy constructor/assignment utility function create doesn't work (you'd want to iterate over rhs and push_back repeatedly, you can't use an iterator to push on new values
The lack of const accessors means that utility function can't be made to work; it would need a const iterator type to iterate rhs with a guarantee that it would not violate the const List& requirement, but you only defined mutating iterator functions
But that it performance optimizations and correctness in code you're not exercising; you can fix that once you're satisfied with the new code.
I've your code (though the fix to copy construction/assignment is an egregious hack using const_cast to brute force around the lack of const safe iteration); I also added a couple tests to show that copy construction and assignment work. Take a look.
The problem (a problem?) is in remove(): you don't check if head is NULL (head case), if tail is NULL (tail case) and if n->previous and n->next are null (generic case)
I suggest this remove()
void remove(Node *n){
if(n == NULL) return;
if(n == head){
head = n->next;
if ( head )
head->previous =NULL;
}
else if(n == tail){
tail = n->previous;
if ( tail )
tail->next = NULL;
}
else{
if ( n->previous )
n->previous->next = n->next;
if ( n->next )
n->next->previous = n->previous;
}
delete n;
}

Making a copy constructor more flexible for ADT queue

I have constructed a copy constructor for an ADT queue. The copy constructor works fine. I would want to improve my code, but I don't really know how to shorten it to make it more flexible. Code is given below:
template <typename T>
Queue <T>::Queue(const Queue & other)
{
if (other.first == nullptr)
{
first = nullptr;
nrOfElements = 0;
}
else
{
Node* saveFirst;
Node* walker;
first = other.first;
walker = new Node(first->data);
saveFirst = walker;
while (first->next != nullptr)
{
walker->next = new Node(first->next->data);
walker = walker->next;
first = first->next;
}
walker->next = nullptr;
first = saveFirst;
}
this->nrOfElements = other.nrOfElements;
}
The class Queue also contains an inner private Node class which contains the pointers first, next, etc:
private:
int nrOfElements;
class Node
{
public:
Node* next;
T data;
Node(T data)
{
this->data = data;
}
};
Node* first;
So, I would appreciate any suggestions/examples of how the copy constructor code above could be improved, as I'm a bit lost on the task.
try this one
may be in easier than your code
template <typename T>
Queue <T>::Queue(const Queue & other)
{
Front =NULL;Rear=NULL;Count=0;
Node *p= other.Front;
while(p!=NULL){
EnQueue(p->GetData());
Rear->SetData(p->GetData());
p=p->next;
}
}
First we set NULL value to Front & Rear and 0 for Count second make new Node to get the data from every Node in old Queue and copy it to the new Queue.

C++ Templates - LinkedList

EDIT -- Answered below, missed the angled braces. Thanks all.
I have been attempting to write a rudimentary singly linked list, which I can use in other programs. I wish it to be able to work with built-in and user defined types, meaning it must be templated.
Due to this my node must also be templated, as I do not know the information it is going to store. I have written a node class as follows -
template <class T> class Node
{
T data; //the object information
Node* next; //pointer to the next node element
public:
//Methods omitted for brevity
};
My linked list class is implemented in a seperate class, and needs to instantiate a node when adding new nodes to the end of the list. I have implemented this as follows -
#include <iostream>
#include "Node.h"
using namespace std;
template <class T> class CustomLinkedList
{
Node<T> *head, *tail;
public:
CustomLinkedList()
{
head = NULL;
tail = NULL;
}
~CustomLinkedList()
{
}
//Method adds info to the end of the list
void add(T info)
{
if(head == NULL) //if our list is currently empty
{
head = new Node<T>; //Create new node of type T
head->setData(info);
tail = head;
}
else //if not empty add to the end and move the tail
{
Node* temp = new Node<T>;
temp->setData(info);
temp->setNextNull();
tail->setNext(temp);
tail = tail->getNext();
}
}
//print method omitted
};
I have set up a driver/test class as follows -
#include "CustomLinkedList.h"
using namespace std;
int main()
{
CustomLinkedList<int> firstList;
firstList.add(32);
firstList.printlist();
//Pause the program until input is received
int i;
cin >> i;
return 0;
}
I get an error upon compilation however - error C2955: 'Node' : use of class template requires template argument list - which points me to the following line of code in my add method -
Node* temp = new Node<T>;
I do not understand why this has no information about the type, since it was passed to linked list when created in my driver class. What should I be doing to pass the type information to Node?
Should I create a private node struct instead of a seperate class, and combine the methods of both classes in one file? I'm not certain this would overcome the problem, but I think it might. I would rather have seperate classes if possible though.
Thanks, Andrew.
While the answers have already been provided, I think I'll add my grain of salt.
When designing templates class, it is a good idea not to repeat the template arguments just about everywhere, just in case you wish to (one day) change a particular detail. In general, this is done by using typedefs.
template <class T>
class Node
{
public:
// bunch of types
typedef T value_type;
typedef T& reference_type;
typedef T const& const_reference_type;
typedef T* pointer_type;
typedef T const* const_pointer_type;
// From now on, T should never appear
private:
value_type m_value;
Node* m_next;
};
template <class T>
class List
{
// private, no need to expose implementation
typedef Node<T> node_type;
// From now on, T should never appear
typedef node_type* node_pointer;
public:
typedef typename node_type::value_type value_type;
typedef typename node_type::reference_type reference_type;
typedef typename node_type::const_reference_type const_reference_type;
// ...
void add(value_type info);
private:
node_pointer m_head, m_tail;
};
It is also better to define the methods outside of the class declaration, makes it is easier to read the interface.
template <class T>
void List<T>::add(value_type info)
{
if(head == NULL) //if our list is currently empty
{
head = new node_type;
head->setData(info);
tail = head;
}
else //if not empty add to the end and move the tail
{
Node* temp = new node_type;
temp->setData(info);
temp->setNextNull();
tail->setNext(temp);
tail = tail->getNext();
}
}
Now, a couple of remarks:
it would be more user friendly if List<T>::add was returning an iterator to the newly added objects, like insert methods do in the STL (and you could rename it insert too)
in the implementation of List<T>::add you assign memory to temp then perform a bunch of operations, if any throws, you have leaked memory
the setNextNull call should not be necessary: the constructor of Node should initialize all the data member to meaningfull values, included m_next
So here is a revised version:
template <class T>
Node<T>::Node(value_type info): m_value(info), m_next(NULL) {}
template <class T>
typename List<T>::iterator insert(value_type info)
{
if (m_head == NULL)
{
m_head = new node_type(info);
m_tail = m_head;
return iterator(m_tail);
}
else
{
m_tail.setNext(new node_type(info));
node_pointer temp = m_tail;
m_tail = temp.getNext();
return iterator(temp);
}
}
Note how the simple fact of using a proper constructor improves our exception safety: if ever anything throw during the constructor, new is required not to allocate any memory, thus nothing is leaked and we have not performed any operation yet. Our List<T>::insert method is now resilient.
Final question:
Usual insert methods of single linked lists insert at the beginning, because it's easier:
template <class T>
typename List<T>::iterator insert(value_type info)
{
m_head = new node_type(info, m_head); // if this throws, m_head is left unmodified
return iterator(m_head);
}
Are you sure you want to go with an insert at the end ? or did you do it this way because of the push_back method on traditional vectors and lists ?
Might wanna try
Node<T>* temp = new Node<T>;
Also, to get hints on how to design the list, you can of course look at std::list, although it can be a bit daunting at times.
You need:
Node<T> *temp = new Node<T>;
Might be worth a typedef NodeType = Node<T> in the CustomLinkedList class to prevent this problem from cropping up again.
That line should read
Node<T>* temp = new Node<T>;
Same for the next pointer in the Node class.
As said, the solution is
Node<T>* temp = new Node<T>;
... because Node itself is not a type, Node<T> is.
And you will need to specify the template parameter for the Node *temp in printlist also.
// file: main.cc
#include "linkedlist.h"
int main(int argc, char *argv[]) {
LinkedList<int> list;
for(int i = 1; i < 10; i++) list.add(i);
list.print();
}
// file: node.h
#ifndef _NODE_H
#define _NODE_H
template<typename T> class LinkedList;
template<typename T>class Node {
friend class LinkedList<T>;
public:
Node(T data = 0, Node<T> *next = 0)
: data(data), next(next)
{ /* vacio */ }
private:
T data;
Node<T> *next;
};
#endif//_NODE_H
// file: linkedlist.h
#ifndef _LINKEDLIST_H
#define _LINKEDLIST_H
#include <iostream>
using namespace std;
#include "node.h"
template<typename T> class LinkedList {
public:
LinkedList();
~LinkedList();
void add(T);
void print();
private:
Node<T> *head;
Node<T> *tail;
};
#endif//_LINKEDLIST_H
template<typename T>LinkedList<T>::LinkedList()
: head(0), tail(0)
{ /* empty */ }
template<typename T>LinkedList<T>::~LinkedList() {
if(head) {
Node<T> *p = head;
Node<T> *q = 0;
while(p) {
q = p;
p = p->next;
delete q;
}
cout << endl;
}
}
template<typename T>LinkedList<T>::void add(T info) {
if(head) {
tail->next = new Node<T>(info);
tail = tail->next;
} else {
head = tail = new Node<T>(info);
}
}
template<typename T>LinkedList<T>::void print() {
if(head) {
Node<T> *p = head;
while(p) {
cout << p->data << "-> ";
p = p->next;
}
cout << endl;
}
}
You Should add new node in this way
Node<T>* temp=new node<T>;
Hope you Solved :)
#include<iostream>
using namespace std;
template < class data > class node {
private :
data t;
node<data > *ptr;
public:
node() {
ptr = NULL;
}
data get_data() {
return t;
}
void set_data(data d) {
t = d;
}
void set_ptr(node<data > *p) {
ptr = p;
}
node * get_ptr() {
return ptr;
}
};
template <class data > node < data > * add_at_last(data d , node<data > *start) {
node< data > *temp , *p = start;
temp = new node<data>();
temp->set_data(d);
temp->set_ptr(NULL);
if(!start) {
start = temp;
return temp;
}
else {
while(p->get_ptr()) {
p = p->get_ptr();
}
p->set_ptr(temp);
}
}
template < class data > void display(node< data > *start) {
node< data > *temp;
temp = start;
while(temp != NULL) {
cout<<temp->get_data()<<" ";
temp = temp->get_ptr();
}
cout<<endl;
}
template <class data > node < data > * reverse_list(node<data > * start) {
node< data > *p = start , *q = NULL , *r = NULL;
while(p->get_ptr()) {
q = p;
p = p->get_ptr();
q->set_ptr(r);
r = q;
}
p->set_ptr(r);
return p;
}
int main() {
node < int > *start;
for(int i =0 ; i < 10 ; i ++) {
if(!i) {
start = add_at_last(i , start);
}
else {
add_at_last(i , start);
}
}
display(start);
start = reverse_list(start);
cout<<endl<<"reverse list is"<<endl<<endl;
display(start);
}