I am creating a class LinkedList. I am having difficulty adding another node to my list.
Here is what I have so far:
template<typename T>
class LinkedList
{
private:
T element;
T *next;
public:
LinkedList();
LinkedList(T element);
void add(LinkedList<T> &otherList);
void print();
};
template<typename T>
LinkedList<T>::LinkedList()
{
next = NULL;
}
template<typename T>
LinkedList<T>::LinkedList(T element)
{
this->element = element;
next = NULL;
}
template<typename T>
void LinkedList<T>::add(LinkedList<T> &otherList)
{
next = &otherList;
}
template<typename T>
void LinkedList<T>::print()
{
LinkedList<T> *current = this;
while (current != NULL)
{
std::cout << current->element;
current = current->next;
}
}
int main()
{
LinkedList<std::string> myFirst("First");
LinkedList<std::string> mySecond("Second");
myFirst.add(mySecond);
myFirst.print();
return 0;
}
This works however if I make the change:
void add(const LinkedList<T> &otherList);
template<typename T>
void LinkedList<T>::add(const LinkedList<T> &otherList)
{
next = &otherList; //now an error right here
}
Then I get an error stating:
Assigning to 'LinkedList<std::__1::basic_string<char> > *' from incompatible type 'const LinkedList<std::__1::basic_string<char> > *'
Why is it I get this error?
next is a T*, and you're trying to assign a const LinkedList<T>* to it.
I suppose you meant something like next = &(otherList.element) (though even then I think your list semantics are somewhat broken — elements shouldn't typically be shared by multiple containers unless you're very, very clear about the ownership semantics).
Contrary to your claims, your first program doesn't work either for the same reason.
Related
I am learning about linked lists, and I decided to try implementing linked lists in C++ on my own.
I made a Node class with attributes int val and Node* ptr.
Then I made a Linked_list class with the attribute first_node, and the constructor functions work.
The append() function 'appends' a node to the list (like in Python). I first thought of just making ptr a reference to the node's pointer and then changing it when its null, but references once made, can't be changed to refer to any other variable, so I made another variable prev_ptr that points to the Node's pointer (which makes it a Node**).
Every loop, it checks if ptr is NULL, if not, ptr and prev_ptr get updated to the next Node's pointer value, and the address of the next Node's pointer value, respectively.
This keeps happening until it finds a null pointer, and then changes it to the inputted node's address.
But I'm getting an error saying:
Exception thrown: write access violation. prev_ptr was 0x4.
I can't figure out what is wrong.
Classes:
#include <iostream>
#include <string>
#include <cmath>
class Node {
public:
int val;
Node* ptr = nullptr;
Node(int Val = NULL) {
val = Val;
}
};
class Linked_list {
public:
Node first_node;
Linked_list(int F) {
Node f(F);
first_node = f;
}
void append(Node& element) {
Node* ptr = first_node.ptr;
Node** prev_ptr = &first_node.ptr;
while (true) {
if (ptr == nullptr) {
*prev_ptr = &element;
break;
}
ptr = (*ptr).ptr;
prev_ptr = &((*ptr).ptr);
}
}
};
main()
int main() {
Linked_list list(5);
Node three(3);
list.append(three);
Node four(4);
list.append(four);
return 0;
}
ptr = (*ptr).ptr;
prev_ptr = &((*ptr).ptr);
First you advance ptr to the next node. Then, you use ptr again forgetting that it has already been advanced: (*ptr).ptr now points two nodes forward, and we don't know if we can go that much far.
Perhaps you need to swap the assignments.
prev_ptr = &((*ptr).ptr);
ptr = (*ptr).ptr;
(Further, why not ptr->ptr?)
Okay, you're doing a few things in an odd fashion. First, your Linked_List should probably NOT have a Node for firstNode. It should have a Node *. After all, an empty list is possible. So is (normally) deleting the first node. Also, there's an informal naming convention of calling it head. There's also a standard convention of calling the link in your Node next rather than ptr.
But there are two simpler methods for your append() method. First, you can also keep a Node * tail in Linked_List. This is common. It points to the last node in the list. If you do that, then append looks like:
void append(Node &nodeToAppend) {
if (head == nullptr) {
head = &nodeToAppend;
tail = &nodeToAppend;
}
else {
tail->next = nodeToAppend;
tail = &nodeToAppend;
}
}
However, it's also worthwhile to be able to insert anywhere or append without a tail:
void append(Node &nodeToAppend) {
if (head == nullptr) {
head = &nodeToAppend;
}
else {
Node *ptr = head;
while (ptr->next != nullptr) {
ptr = ptr->next;
}
ptr->next = &nodeToAppend;
}
}
An insert in some sort of sorted order would be nearly identical, although slightly different. The while-loop would look like:
while (ptr->next != nullptr && ptr->value < nodeToAppend.value) ...
but would otherwise be identical.
This code doesn't solve your immediate issue, but answers a question raised in the comments.
Linked lists are usually (where I taught) a 300-400 level assignment. There are a lot of principles that one must be competent in to write a decent linked list. First, I'll show the main.cpp and its output.
main.cpp
#include "list.hpp"
#include <iostream>
template <typename Container>
void print(Container& container, std::ostream& sout = std::cout)
{
for (auto i : container) {
sout << i << ' ';
}
sout << '\n';
}
int main()
{
List<int> list;
for (int i = 1; i <= 10; ++i) {
list.push_back(i);
}
print(list);
list.erase(list.find(4));
print(list);
list.erase(list.find(1));
print(list);
list.erase(list.find(10));
print(list);
}
Output:
1 2 3 4 5 6 7 8 9 10
1 2 3 5 6 7 8 9 10
2 3 5 6 7 8 9 10
2 3 5 6 7 8 9
It doesn't test every aspect of the linked list, but it serves to demonstrate what a user should be expected to work with. Users will want to interact directly with the list and its iterators in C++. You create a Node, and then add the Node to your list. That's a level of DIY that no user wants to be bothered with. In the code below, you'll see that a Node is still used, but it only exists within the List class. Users will never see a Node.
You can look in functions like push_back() (similar to your append) for specific answers related to your question.
To explain it a bit more, the pointers are key. Yes, I declare a local Node* that will go out of scope, but the object created continues to exist on the heap. And the list is able to keep track of these Nodes due to how linked lists work, namely that the Nodes know where their neighbors live (hold their addresses).
There is also a List<T>::iterator class. In the declaration, functions marked as // minimum are required if you want to use your linked list in a range-based for loop. The other functions do work toward satisfying the requirements of LegacyBidirectionalIterator; this is the level of iterator used by std::list in the C++ Standard Library.
The code below should only be considered a decent example (Hopefully not too presumptuous on my part). It is lacking some functionality that's found in std::list, and likely does a few things in non-optimal manners. A big thing that will need tweaking is removing the member function find() and make the class work with std::find().
list.hpp
#ifndef MY_LIST_HPP
#define MY_LIST_HPP
#include <algorithm> // std::swap
#include <cstddef> // std::size_t
/*
* Pre-declare template class and friends
*/
template <typename T>
class List;
template <typename T>
void swap(List<T>& lhs, List<T>& rhs);
/*
* List Class Declaration
*/
template <typename T>
class List {
public:
List() = default;
List(T val);
List(const List& other);
List(List&& other);
~List();
void push_front(T val);
void push_back(T val);
class iterator;
iterator begin();
iterator end();
iterator find(T val);
std::size_t size() const;
iterator erase(iterator toErase); // Implement
void clear();
bool operator=(List other);
friend void swap<T>(List& lhs, List& rhs);
private:
struct Node {
T data;
Node* prev = nullptr;
Node* next = nullptr;
Node(T val) : data(val) {}
};
Node* m_head = nullptr;
Node* m_tail = nullptr;
std::size_t m_size = 0;
// Helper functions
void make_first_node(T val);
Node* find_node(T val);
};
/*
* List Iterator Declaration
*/
template <typename T>
class List<T>::iterator {
public:
iterator() = default;
iterator(List<T>::Node* node); // minimum
T& operator*(); // minimum
iterator& operator++(); // minimum
iterator operator++(int);
iterator& operator--();
iterator operator--(int);
bool operator==(const iterator& other); // minimum
bool operator!=(const iterator& other); // minimum
private:
Node* m_pos = nullptr;
};
/*
* List Implementation
*/
template <typename T>
List<T>::List(T val) : m_head(new Node(val)), m_tail(m_head), m_size(1) {}
template <typename T>
List<T>::List(const List<T>& other) {
m_head = new Node((other.m_head)->data);
m_tail = m_head;
m_size = 1;
Node* walker = (other.m_head)->next;
while (walker) {
push_back(walker->data);
++m_size;
walker = walker->next;
}
}
template <typename T>
List<T>::List(List&& other) : List() {
swap(*this, other);
}
template <typename T>
List<T>::~List() {
clear();
}
template <typename T>
void List<T>::push_front(T val)
{
if (!m_head) {
make_first_node(val);
return;
}
Node* tmp = new Node(val);
tmp->next = m_head;
m_head->prev = tmp;
m_head = tmp;
++m_size;
}
template <typename T>
void List<T>::push_back(T val) {
if (!m_head) {
make_first_node(val);
return;
}
Node* tmp = new Node(val);
tmp->prev = m_tail;
m_tail->next = tmp;
m_tail = tmp;
++m_size;
}
template <typename T>
typename List<T>::iterator List<T>::begin() {
return iterator(m_head);
}
template <typename T>
typename List<T>::iterator List<T>::end() {
return iterator(nullptr);
}
template <typename T>
typename List<T>::iterator List<T>::find(T val) {
return iterator(find_node(val));
}
template <typename T>
std::size_t List<T>::size() const {
return m_size;
}
template <typename T>
typename List<T>::iterator List<T>::erase(typename List<T>::iterator toErase)
{
Node* node = find_node(*toErase);
if (node->prev) {
node->prev->next = node->next;
} else {
m_head = node->next;
}
if (node->next) {
node->next->prev = node->prev;
} else {
m_tail = node->prev;
}
Node* toReturn = node->next;
delete node;
return toReturn;
}
template <typename T>
void List<T>::clear() {
Node* tmp = m_head;
while (m_head) {
m_head = m_head->next;
delete tmp;
tmp = m_head;
}
m_tail = nullptr;
m_size = 0;
}
template <typename T>
bool List<T>::operator=(List other) {
swap(*this, other);
return *this;
}
template <typename T>
void List<T>::make_first_node(T val) {
m_head = new Node(val);
m_tail = m_head;
m_size = 1;
}
template <typename T>
typename List<T>::Node* List<T>::find_node(T val) {
if (!m_head) {
return nullptr;
}
Node* walker = m_head;
while (walker != nullptr && walker->data != val) {
walker = walker->next;
}
return walker;
}
template <typename T>
void swap(List<T>& lhs, List<T>& rhs) {
using std::swap;
swap(lhs.m_head, rhs.m_head);
swap(lhs.m_tail, rhs.m_tail);
swap(lhs.m_size, rhs.m_size);
}
/*
* List Iterator Implementation
*/
template <typename T>
List<T>::iterator::iterator(Node* node) : m_pos(node) {}
template <typename T>
T& List<T>::iterator::operator*() {
return m_pos->data;
}
template <typename T>
typename List<T>::iterator& List<T>::iterator::operator++() {
m_pos = m_pos->next;
return *this;
}
template <typename T>
typename List<T>::iterator List<T>::iterator::operator++(int) {
iterator tmp(m_pos);
++(*this);
return tmp;
}
template <typename T>
typename List<T>::iterator& List<T>::iterator::operator--() {
m_pos = m_pos->prev;
return *this;
}
template <typename T>
typename List<T>::iterator List<T>::iterator::operator--(int) {
iterator tmp(m_pos);
--(*this);
return tmp;
}
template <typename T>
bool List<T>::iterator::operator==(const iterator& other) {
return m_pos == other.m_pos;
}
template <typename T>
bool List<T>::iterator::operator!=(const iterator& other) {
return !(*this == other);
}
#endif
Code and explaination
I think this code should work as well, so there is no need to have two pointers. It's based on an example of “good taste” that Linus Torvalds gave in an interview.
void append(Node &element)
{
Node** cursor = &first_node.ptr;
while ((*cursor) != nullptr)
cursor = &(*cursor)->ptr;
*cursor = &element;
}
It eliminates the need for multiple pointers, it eliminates edge cases and it allows us to evaluate the condition of the while loop without having to let go of the pointer that points to the next element. This allows us to modify the pointer that points to NULL and to get away with a single iterator as opposed to ptr and prev_ptr.
Naming conventions
Also the norm is to call the first node in the linked list head and to call the pointer to the next node next instead of ptr, so I will rename them in the following code.
void append(Node &new)
{
Node** cursor = &head.next;
while ((*cursor) != nullptr)
cursor = &(*cursor)->next;
*cursor = &new;
}
Hi I'm trying to implement a simple singly linked list using smart pointers, here is what I have so far, I opted with using C++'s shared_ptr but I read that a unique_ptr would be more appropriate for this case but, I don't really know how you would iterate over the list (i.e currentNode = currentNode->next) to get to the end of the list in order to insert an element using a unique_ptr. Here is the code I have so far:
template <typename T>
class LinkedList;
template <typename T>
class ListNode
{
public:
ListNode() : _data(T()) {}
explicit ListNode(const T& value) : _data(value) {}
friend class LinkedList < T > ;
private:
T _data;
shared_ptr<ListNode<T>> _next;
};
template <typename T>
class LinkedList
{
public:
void push_back(const T& value)
{
if (_root)
{
shared_ptr<ListNode<T>> currentNode(_root);
while (currentNode->_next != nullptr)
{
currentNode = currentNode->_next;
}
currentNode->_next = make_shared<ListNode<T>>(value);
}
else
{
// If the list is completely empty,
// construct a new root (first element)
_root = make_shared<ListNode<T>>(value);
}
}
void print() const
{
shared_ptr<ListNode<T>> currentNode(_root);
while (currentNode != nullptr)
{
cout << currentNode->_data << " ";
currentNode = currentNode->_next;
}
cout << endl;
}
private:
shared_ptr<ListNode<T>> _root;
};
If using unique_ptrs are the better way to go for this program, could you illustrate how I would get past the iterating problem? Since unique_ptrs can't be assigned, how would I do the code block:
shared_ptr<ListNode<T>> currentNode(_root);
while (currentNode->_next != nullptr)
{
currentNode = currentNode->_next;
}
currentNode->_next = make_shared<ListNode<T>>(value);
using unique_ptrs instead of shared_ptrs? Thanks!
Your loop with std::unique_ptr may look like:
// Iteration doesn't own resource, so no unique_ptr here.
ListNode<T>* currentNode(_root.get());
while (currentNode->_next != nullptr)
{
currentNode = currentNode->_next.get();
}
currentNode->_next = make_unique<ListNode<T>>(value);
Code for Reversing Linked List with Recursion, using STL
#include<iostream>
#include<conio.h>
#include<list>
using namespace std;
template<typename T>
class node
{
public:
T data;
node<T> *next;
node(){ next = NULL; }
node(const T& item, node<T> *nextnode = NULL)
{
data = item;
next = nextnode;
}
};
template<typename T>
class Reverse_list
{
private:
node<T> *head;
void reverse(node<T> *front);
public:
Reverse_list(){ head = NULL; }
//template<typename T>
void Reverse();
template<typename T>
void Display( list<T>& alist );
};
int main()
{
Reverse_list <int> rl;
list<int> intlist;
int size, no;
cout << "Size of List ?? ";
cin >> size;
for (int i = 1; i <= size; i++)
{
cout << "Enter the " << i <<" "<< "element";
cin >> no;
intlist.push_front(no);
}
rl.Display(intlist);
rl.Reverse();
rl.Display(intlist);
_getch();
return 0;
}
template<typename T>
void Reverse_list<T>::Display(list<T>& alist)
{
list<int>::iterator iter = alist.begin();
while (iter != alist.end())
{
cout << *iter << " ";
iter++;
}
}
template<typename T>
void Reverse_list<T>::reverse(node<T> *front)
{
if (front->next == NULL)
{
head = front;
return;
}
reverse(front->next);
node<int> *back = front->next;
back->next = front;
front->next = NULL;
}
template<typename T>
void Reverse_list<T>::Reverse()
{
reverse(head);
}
The above code generates 2 errors.
Error 1) No instance of function template matches the argument list. ( No error number.)
If I remove line 1 ( mentioned in a code ) then above error is no more. ( Why? )
Error 2) C2783: 'void Reverse_list::Reverse1(void)' : could not deduce template argument for 'T'
How to solve above errors.
In above program , I wanted to pass " head" node ( which is private ) as
argument to Reverse function. But we can not access private member outside of the class. So I passed indirectly. Is this a correct way of passing ?? Or there is some other way of accessing private data ??
I'm not sure to understand your intentions but...
You're trying to declare a method (reverse()) inside another method (Reverse()) ? Uhmmm....
We return to this later.
Imagine that the following instruction is correct instruction of Reverse_list<T>::Reverse()
node<T> *back = front->next;
Why you declare back as a pointer to a generic Node<T> when you assign front->next (so a specific Node<int>) to it?
If you define back as a node<int> pointer, the method Reverse() has no longer reason to be a template (dependant from T) method. And you can avoid both errors.
With your actual code, when you call
rl.Reverse();
you call a template method but the compiler doesn't know how to determine the type T. You could explicit it in this way
rl.Reverse<int>();
but, as written before, I thik it's better if you remove the whole template part.
Or, alternatively, you can transform the whole class in a template class; where head is a pointer to a generic Node<T>, not a specifica Node<int>.
Something like (if I understand correctly your intentions)
template <typename T>
class Reverse_list
{
private:
node<T> *head;
void reverse (node<T> * front);
public:
Reverse_list() : head(NULL)
{ }
void Reverse();
void Display(list<T>& alist);
};
template<typename T>
void Reverse_list<T>::reverse (node<T> * front)
{
if (front->next == NULL)
{
head = front;
return;
}
reverse(front->next);
node<T> *back = front->next;
back->next = front;
front->next = NULL;
}
template<typename T>
void Reverse_list<T>::Reverse()
{ reverse(head); }
In this case, in main(), rl should be declared as
Reverse_list<int> rl;
fixing T as int, and the call to Reverse() should be
rl.Reverse();
--- EDIT 2016.05.10 ---
With the "template Reverse_list" solution, you should correct three points (at last).
1) in Reverse_list class declaration, you have commented the template<typename T> row before void Reverse(); good; you should delete (comment) the same line (for the same reason) before void Display( list<T>& alist );; so the class become
template<typename T>
class Reverse_list
{
private:
node<T> *head;
void reverse(node<T> *front);
public:
Reverse_list(){ head = NULL; }
//template<typename T>
void Reverse();
//template<typename T>
void Display( list<T>& alist );
};
2) Display() now is a method of a templated class; so the line
list<int>::iterator iter = alist.begin();
become
list<T>::iterator iter = alist.begin();
3) reverse() now is a method of a templated class; so the line
node<int> *back = front->next;
become
node<T> *back = front->next;
So I am trying to create my own implementation file which contains instructions for a Queue. I decided to use a linked list to implement the Queue class, meaning that I need to use my own Node struct. Unfortunately, I am stuck and don't know how to properly include this within the file.
This is what I have so far:
#include <string>
#ifndef NODE
template <class DataType>
struct Node
{
DataType data;
Node *next;
};
#endif
template <class DataType>
class Queue
{
public:
Queue();
bool isEmpty() const;
void push(const DataType& parameter);
bool peek(DataType& parameter) const;
bool pop(DataType& parameter);
void makeEmpty();
private:
Node<DataType>* front;
Node<DataType>* end;
};
template <class DataType>
Queue<DataType>::Queue()
: front(0), end(0)
{
}
template <class DataType>
bool Queue<DataType>::isEmpty() const {return 0 == front;}
template <class DataType>
void Queue<DataType>::push(const DataType& parameter)
{
Node<DataType>* node = new Node<DataType>;
node->data = parameter;
node->next = 0;
if (end) end->next = node;
else front = node;
end = node;
}
template <class DataType>
bool Queue<DataType>::peek(DataType& parameter) const
{
if (0 == front) return false; // failed
parameter = front->data;
return true; // success
}
template <class DataType>
bool Queue<DataType>::pop(DataType& parameter)
{
if (0 == front) return false; // failed
parameter = front->data;
Node<DataType>* p = front->next;
delete front;
front = p;
if (front == 0) end = 0;
return true; // success
}
template <class DataType>
void Queue<DataType>::makeEmpty()
{
end = 0;
Node<DataType>* p;
while (front)
{
p = front->next;
delete front;
front = p;
}
}
I'm not sure if I am enclosing the struct by the #ifndef correctly (i'm not even sure if this is the route I should be taking :/), should I be doing something similar to this or should I be doing something else with the code for the struct?
You can just drop the #ifdef/#endif entirely
This is a class template and it may occur many times in several tranlation units, as long as all the occurrences are identical (One Definition Rule)
Alternative
Since Node<> is purely a private concern, I'd make it a nested struct.
Here's a little demo making this more 'modern C++' style.
Edit Thanks to #R.MartinhoFernandes for showing a few more improvements and for reviewing this.
#include <memory>
template <typename T>
struct Queue {
Queue() : front(), end(/*nullptr*/) {}
// Copy-And-Swap idiom
// see http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Copy-and-swap
// or http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
void swap(Queue& q) noexcept {
using std::swap;
swap(q.front, front);
swap(q.end, end);
}
Queue(Queue const& q) : front(), end() {
for(auto it=q.front.get(); it; it=it->next.get())
push(it->data);
}
Queue& operator=(Queue q) {
std::swap(*this, q);
return *this;
}
// end Copy-and-swap
// prevent stack overflows in ~Node if the list grows large (say >1k elements)
~Queue() { clear(); }
bool isEmpty() const {
return !front;
}
void push(T const& data) {
Ptr node(new Node(data));
if (end)
end->next = std::move(node);
else
front = std::move(node);
end = node.get();
}
bool peek(T& data) const {
if(front) data = front->data;
return front.get();
}
bool pop(T& data) {
if(!front) return false;
data = front->data;
front = std::move(front->next);
if(!front) end = nullptr;
return true;
}
void clear() {
end = nullptr;
while(front) front = std::move(front->next);
}
private:
struct Node;
typedef std::unique_ptr<struct Node> Ptr;
struct Node {
Node(T data) : data(std::move(data)), next() {}
T data;
Ptr next;
};
Ptr front;
Node* end;
};
#include <iostream>
int main(int argc, const char *argv[]) {
Queue<int> test;
test.push(1);
test.push(2);
test.push(3);
test.push(5);
test.clear();
test.push(32028);
test.push(10842);
test.push(1839);
test.push(23493);
test.push(9857);
int x;
test.peek(x);
while(test.pop(x)) {
std::cout << x << '\n';
}
}
Note: Perhaps the code in push has been golfed a bit too far, but hey, it shows you how modern C++ requires much less handholding (even without std::make_unique).
Note how I think Clang correctly handles the following version (i.e. with implicit std::move):
void push(const DataType& parameter) {
end = ((end? end->next : front) = Ptr(new Node(parameter))).get();
}
I'm not quite sure why gcc rejects it.
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);
}