C++ Templates - LinkedList - c++

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);
}

Related

How to prevent memory leak in generalized linked list?

I've implemented my own linked list data structure. Data is stored inside Node struct. Code is as follows
// NODE
template <typename T>
struct Node
{
T data;
Node<T> *next;
Node(T);
};
template <typename T>
Node<T>::Node(T d) : data(d), next(NULL) {}
// LIST
#include "node.cpp"
template <typename T>
class List
{
Node<T> *head;
int size;
public:
List(); // Default constructor
List(const List &); // Copy constructor
void push_back(const T &); // Insert element to the end of the list
int get_size() const; // Get the current size of the list
T &operator[](int) const; // Overload [] operator
void operator=(const List &); // Overload = operator
~List(); // Destructor
};
template <typename T>
List<T>::List() : head(NULL), size(0) {}
template <typename T>
List<T>::List(const List &list) : head(NULL), size(0)
{
for (int i = 0; i < list.size; i++)
push_back(list[i]);
}
template <typename T>
void List<T>::push_back(const T &data)
{
// Create new Node with data
Node<T> *nn = new Node<T>(data);
// Find insert position
if (head == NULL)
{
head = nn;
size++;
return;
}
Node<T> *traverse = head;
while (traverse->next)
traverse = traverse->next;
// Traverse points to end of the list
traverse->next = nn;
size++;
}
template <typename T>
int List<T>::get_size() const
{
return size;
}
template <typename T>
T &List<T>::operator[](int index) const
{
int count = 0;
Node<T> *traverse = head;
while (traverse && count < index)
{
traverse = traverse->next;
count++;
}
return traverse->data;
}
template <typename T>
void List<T>::operator=(const List<T> &list)
{
Node<T> *traverse = head;
while (head)
{
traverse = head;
head = head->next;
delete traverse;
}
size = 0;
for (int i = 0; i < list.getSize(); i++)
push_back(list[i]);
}
template <typename T>
List<T>::~List()
{
Node<T> *traverse = head;
while (head)
{
traverse = head;
head = head->next;
delete traverse;
}
}
Problem is with memory leak. Consider the following main file
#include "list.cpp"
using namespace std;
List<int *> l;
void func()
{
int *i = new int[2];
i[0] = 1;
i[1] = 2;
l.push_back(i);
}
int main()
{
func();
return 0;
}
This program has memory leak according to Valgrind. It is because that Node does not have a destructor so it can not delete data inside it. However, I can not add a destructor to Node because, suppose that I am using List<int> so it is an error to delete something that was not dynamically allocated. In short, whenever I use a dynamically allocated data type for List, I get memory leak. How can I overcome this situation? Thanks.
The leak in your example has nothing to with the list. You leak the same with:
void func()
{
int *i = new int[2];
i[0] = 1;
i[1] = 2;
}
You have to delete what you created via new and delete[] what you created via new[]. To fix the leak:
void func()
{
int *i = new int[2];
i[0] = 1;
i[1] = 2;
l.push_back(i);
delete [] i;
}
However, note that then after the delete[] you have a dangling pointer in the list.
It is not the Lists buisness to delete objects when you push raw pointers to it. The list cannot know if those are owning pointers or not. For example:
void func()
{
int i = 0;
l.push_back(&i);
}
No need to delete anything here. (Though, same here: once the function returns you have a dangling pointer in the list)
Neither of the abvove is really "ok". Don't use raw owning pointers! Use smart pointers instead. And if you want a list of integers then use a List<int> (or rather a std::list<int>).
Use std::unique_ptr as the data type for the nodes, eg:
List<std::unique_ptr<int[]>> l;
When each node is destroyed, its destructor will destroy its unique_ptr data, which will in turn call delete[] on the int* pointer it is holding.

Adding function templates to classes

So far, I have implemented a basic LinkedList. This works, but only for integers, and I would like it to work for any type.
I'm trying to get it to work for first any same type (i.e a LinkedList of just strings, or then just ints). After, I would like it to find a way of making it a LinkedList of anything (containing strings, then ints, then longs, all in one list).
#include <iostream>
struct Node{
Node(int value);
Node *next;
int data;
};
Node::Node(int value){
this->data = value;
this->next = nullptr;
}
struct LinkedList{
Node *head;
LinkedList();
void push_back(int value);
void print();
};
LinkedList::LinkedList(){
this->head = nullptr;
}
void LinkedList::push_back(int value){
Node *n = new Node(value);
if(this->head == nullptr){
this->head = n;
} else {
Node *cursor = this->head;
while (cursor->next != nullptr){
cursor = cursor->next;
}
cursor->next = n;
}
}
void LinkedList::print(){
Node *cursor = this->head;
while(cursor != nullptr){
std::cout << cursor->data << '\n';
cursor = cursor->next;
}
}
int main(){
LinkedList l = LinkedList();
l.push_back(1);
l.push_back(2);
l.print();
}
The above works, however, only for ints.
I'm knew, but I think the way is to use templates, however, doing so, I seem to be doing overkill? and it doesn't compile? Is there a cleaner was to do this?
#include <iostream>
template <typename T>
struct Node {
Node(T value);
int data;
Node<T> *next;
};
template <typename T>
Node<T>::Node(T value){
this->next = nullptr;
this->data = value;
}
template <typename T>
class LinkedList{
public:
LinkedList();
Node<T> *head;
void push_back(T data);
void print();
};
template <typename T>
LinkedList<T>::LinkedList(){
this->head = nullptr;
}
template <typename T>
void LinkedList<T>::push_back(T data){
Node *n = new Node(data);
if(this->head == nullptr){
this->head = n;
} else {
Node *cursor = this->head;
while(cursor->next != nullptr){
cursor = cursor->next;
}
cursor->next = n;
}
}
template <typename T>
void LinkedList<T>::print(){
Node *cursor = this->head;
while(cursor != nullptr){
std::cout << cursor->data << '\n';
cursor = cursor->next;
}
}
int main(){
LinkedList<T> *list = new LinkedList<T>();
list->push_back(1);
list->push_back(2);
list->push_back(3);
}
When declaring template classes, you use the "T" as a 'type placeholder' in the declaration and implementation (as you have done). However, when you want to actually use an object of the templated class, you replace the "T" with the actual type you want.
So, in your main (assuming you want an int type), you would have code like this:
int main(){
LinkedList<int> *list = new LinkedList<int>(); // THIS object uses "int" wherever "T" occurs in the declaration/implementation
list->push_back(1);
list->push_back(2);
list->push_back(3);
}
I also noticed a 'possible/probable error' in your struct declaration, where you specified that the data member is of (fixed) type int; maybe (almost certainly, actually, as you later assign a "T"-type value to it) you want this to vary according to the actual type requested? If so, make the following change:
template <typename T>
struct Node {
Node(T value);
// int data;
T data; // Data will be whatever "T" is when an object is created.
Node<T> *next;
};
Feel free to ask for further clarification and/or explanation.

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

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...

Reversing Linked List with Recursion, using STL

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;

C++ does not name a type

creating some old data structures in C++. Currently I am having an issue with a doubly-linked list class:
List.h:
template <class T>
class List{
private:
int size;
struct listNode{
T data;
listNode* next;
listNode* prev;
listNode(T newData);
};
listNode * head;
listNode * tail;
listNode * curr;
listNode * find(listNode * place, int k);
void removeCurrent(listNode * temp);
public:
List();
int getSize() const;
void insert(int loc, T data);
void remove(int loc);
T const & getItem(int loc) const;
void print();
};
List.cpp:
#include "List.h"
#include <iostream>
using namespace std;
template<class T>
List<T>::List(){
size = 0;
head->next = tail;
head->prev = NULL;
tail->prev = head;
tail->next = NULL;
}
// getSize: public method that returns the size of the list
template<class T>
int List<T>::getSize() const {
return size;
}
// insert: public method that inserts data into the list
template<class T>
void List<T>::insert(int loc, T data){
if(loc <1){
cout<<"Invalid Location"<<endl;
return;
}
curr = find(head,loc-1);
listNode * newNode = new listNode(data);
newNode->next = curr->next;
newNode->prev = curr;
newNode->next->prev = newNode;
curr->next = newNode;
size++;
}
// remove: public method that inserts data into the list
template<class T>
void List<T>::remove(int loc){
if(loc <1){
cout<<"Invalid Location"<<endl;
return;
}
curr = find(head,loc); // Find the node infront of the target
removeCurrent(curr); // Remove that node
}
// removeCurrent: helper function that removes the current node
template<class T>
void List<T>::removeCurrent(listNode* temp){
listNode* t = temp->next;
temp->data = t->data; // HACK: take data from next node
temp->next = t->next;
t->next->prev = temp;
delete t;
t=NULL;
size--;
}
// find: private helper function that returns a pointer to the k-1 node
template<class T>
listNode * List<T>::find(listNode * place, int k){
if((k==0) || (place==NULL))
return place;
else return find(place->next,k-1);
}
// getItem: returns data at location loc
template<class T>
T const& List<T>::getItem(int loc) const{
curr = find(head,loc);
return curr->data;
}
// print: prints the sequence of variables in the list
template<class T>
void List<T>::print()
{
curr = head;
while(curr->next != tail){
curr = curr->next;
cout<<curr->data<<endl;
}
}
//listNode constructor
template<class T>
List<T>::listNode::listNode(T newdata):data(newdata),next(NULL),prev(NULL)
{}
The error I'm getting is the following:
error: 'listNode' does not name a type.
I have tried different suggestions offered in similar troubleshooting posts, but I'm still getting this error. I have a main.cpp that includes List.cpp, but it's practically empty.
You're going to have to specify which listNode you're talking about at the find method's return type because you defined it as a member of the List class and you're also going to have to use typename (because List<T> is a dependent scope).
template <class T>
typename List<T>::listNode* List<T>::find(listNode* place, int k)
{
if ((k == 0) || (place == NULL))
return place;
else
return find(place->next, k-1);
}
Assuming you're using c++11, you may also want to use nullptr instead of NULL since its safer and use the initializer list at the List constructor.