(C++) Doubly Linked List Template - Access Problems - c++

I am very new to C++ templates. I am currently working on a project where I need to implement a Doubly Linked List using a template. Here is currently what I have so far:
template<class ItemType>
class SortedList
{
public:
SortedList();
~SortedList();
bool Insert (ItemType toAdd);
bool Delete (ItemType toDelete);
void Print();
private:
SortedList ( const SortedList & copyFrom );
SortedList & operator= ( const SortedList & assignFrom );
struct Node
{
Node ( ItemType item, Node * p = NULL, Node * n = NULL )
{ data = item; prev = p; next = n; }
ItemType data;
Node * prev, * next;
};
Node * list;
};
template<class ItemType>
SortedList<ItemType>::SortedList()
{
list == NULL;
}
template<class ItemType>
SortedList<ItemType>::~SortedList()
{
Node * curr = list;
while ( curr != NULL )
{
Node * tempNext = curr->next;
delete current;
current = tempNext;
}
}
However, in my destructor for example, why can't I access the node elements? The code that is inside that method right now compiled, but does not throw errors. However if I try to use -> on curr, next or prev do not appear. Why do I not have access to these? I feel like I am missing something very obvious here to get started.
Also, how can I initialize list == NULL in the function head, instead of doing it outside of the class?

Don't know why it compiles, but you are using
delete current;
current = tempNext;
Instead of:
delete curr;
curr = tempNext;
Use inline initializer syntax:
class SortedList
{
public:
SortedList()
:
list(nullptr)
{ }
Use the same syntax to initialize Node (it's more optimal)

Related

Fixing memory leaks in a doubly linked list implementation

I read some of the other posts on this topic because there were quite a few, but they didn't really help my situation.
I am getting memory leaks in my implementation of a doubly linked list. I have to make my own so using list is not an option.
here are the two push functions I am using...
template <class T>
void dllist<T>::push_front(T val) {
node* new_node = new node;
new_node->value = val;
new_node->forward = head;
new_node->backward = nullptr;
if (head != nullptr)
head->backward = new_node;
head = new_node;
}
and...
template <class T>
void dllist<T>::push_back(T val) {
node* new_node = new node;
new_node->value = val;
new_node->forward = nullptr;
if (!head)
head = new_node;
else {
node* traveller = head;
while (traveller->forward != nullptr)
traveller = traveller->forward;
traveller->forward = new_node;
new_node->backward = traveller;
}
}
finally, here is my destructor
template <class T>
dllist<T>::~dllist() {
node* current = head;
while (current != nullptr) {
node* forward = current->forward;
delete current;
current = forward;
}
}
In main, I declare an object of type dllist called mylist and I make a few calls to push_front with some integer values and then push_back.
I am using the CRT library to check for leaks and there is a leak at each call to push_back or push_front.
I am confused because I thought I made my destructor correctly. Is there something else Im not seeing?
If anyone could point me in the right direction I'd appreciate it!
Thanks.
MRE
template<class T>
class dllist {
struct node {
T value;
node* forward;
node* backward;
};
node* head;
public:
dllist(); // default constructor
~dllist(); // default destructor
void push_front(T); // push element to the front of the list
void push_back(T); // push element to the back of the list
};
int main() {
{
dllist<int> mylist;
mylist.push_front(10);
mylist.push_front(12);
mylist.push_front(14);
mylist.push_front(16);
mylist.push_front(18);
mylist.push_front(19);
mylist.push_back(11);
mylist.push_back(21);
mylist.push_back(31);
mylist.push_back(41);
mylist.push_back(31);
mylist.push_back(41);
mylist.push_back(222);
}
_CrtDumpMemoryLeaks();
return 0;
}
template <class T>
dllist<T>::dllist() {
head = nullptr;
}

Linked List, copy list to another list

I'm trying to copy a list into another list.
I have this:
template< typename T > class List {
class Node {
public:
T element;
Node *next;
Node( T a_element, Node * a_suivant = nullptr );
virtual ~Node( void );
};
int _taille;
Node * _first;
Node * _last;
public:
List( void );
virtual ~List( void );
int taille( void );
bool empty( void );
I'm trying to create a function that copies a list (using this) into another list and empty the list in argument. I tried to start with emptying the list first to see if it works but I always get segmentation error.
void copyEmpty( List< T > & a_List ){
Node *c = a_List._last;
while(c!=NULL){
Node *t = c->next;
delete c;
c = t;
if(c==a_List._last) c = NULL;
}
a_List._last = NULL;
}
How can I implement the function using next, _last, _first of the list in use (this) and the list in argument (a_List)?
Above of all, it seems you are a C programmer...
Don't use ( void ) in C++!
Use nullptr instead of NULL! (This might become important for overloaded functions taking pointers if you want to make use of std::nullptr_t, the null pointer type, which is defined as decltype(nullptr))
Use, like #david-c-rankin mentioned, a move constructor, or move operator = for your purpose to be compliant with standard design in C++. At least one of them will also work in your template context you mentioned in a comment.
Now coming to the problem: I don't claim that this is a solution since I'm not sure if a got your question right.
template <typename T>
class List {
class Node {
public:
T element;
Node* next;
Node( const T& a_element, Node* a_suivant = nullptr );
virtual ~Node();
};
int _taille;
Node* _first;
Node* _last;
public:
List();
virtual ~List();
int taille();
bool empty();
};
template <class T>
void copyEmpty( List< T > & a_List ){
for(auto pNode = a_List._first; pNode != a_List._last;){
Node* const pNext{ pNode->next };
delete pNode;
pNode = pNext;
}
delete a_List._last;
a_List._last = nullptr;
a_List._first = nullptr;
}
For now I kept your function name. There is still the need of tackling the privateness of _last etc.
I think in your solution you skip deleting the last one.
Maybe you may add a clear specification what _first, _last and your function are / do respectively.

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

No Intellisense for Doubly Linked List Template

I am new to templates in C++ and am working on a project where I need to implement a Doubly Linked List using a template. However, I can't seem to access the node elements next and previous.
For example, in my destructor, I cannot use curr-> to bring up my options of using next or prev. IntelliSense just says, "No members available." Also, I can only find errors during build time...no red lines, warnings, anything appear beforehand. I am curious as to why this is not working....is it a bug or intended? If it is, where is my template incorrect thus far?
template<class ItemType>
class SortedList
{
public:
SortedList();
~SortedList();
bool Insert (ItemType toAdd);
bool Delete (ItemType toDelete);
void Print();
private:
SortedList ( const SortedList & copyFrom );
SortedList & operator= ( const SortedList & assignFrom );
struct Node
{
Node ( ItemType item, Node * p = NULL, Node * n = NULL )
{ data = item; prev = p; next = n; }
ItemType data;
Node * prev, * next;
};
Node * list;
};
template<class ItemType>
SortedList<ItemType>::SortedList()
{
list = NULL;
}
template<class ItemType>
SortedList<ItemType>::~SortedList()
{
Node * curr = list;
while ( curr != NULL )
{
Node * tempNext = curr->next;
delete curr;
curr = tempNext;
}
}
Put the Node struct out of the SortedList like this:
template<typename ItemType>
struct Node
{
Node(ItemType item, Node * p = NULL, Node * n = NULL)
{
data = item; prev = p; next = n;
}
ItemType data;
Node * prev, *next;
};
and then instantiate the template (create the type) inside SortedList like this:
template<class ItemType>
class SortedList
{
public:
//... More code here.
private:
// ... More code here.
Node<ItemType> * list;
};
template<class ItemType>
SortedList<ItemType>::SortedList()
{
list = NULL;
}
template<class ItemType>
SortedList<ItemType>::~SortedList()
{
Node<ItemType> *curr = list;
while (curr != NULL)
{
Node * tempNext = curr->next; // Now this will work.
delete curr;
curr = tempNext;
}
}
The logic I have followed
A template, is not a type. You get the type instantiating the template. Hence the type Node Does not exists until you instantiate the template StortedList since the former is inside the latter.
Also the exact type for Node would be SortedList<ItemType>::Node, and there you can see you can't talk about Node, before compiling the code. Thats why IntelliSense don't "see it".

Accessing an element within my custom linked list class

I'm building my own linked list class and I'm having some issues figuring out how to write some functions to help me traverse this list. This is my first time building a linked list from scratch, so if my approach is unconventional please let me know what might be more conventional.
I'd like write a function, within the List class that allows me to increment to the next element called getNext() as well as one that getPrev();
I wrote getNext like this:
T* getNext(){return next;}
However it tells me next is not declared within the scope. I'd also like to write a function that lets me access and modify the object within the list. I was considering using the bracket operator, but first I need to write a function to return the data member. Perhaps If I take a similar approach as I did within my pop functions.. thinking about it now. However, I'd still appreciate any advice.
Here is my List class:
#ifndef LIST_H
#define LIST_H
//List Class
template <class T>
class List{
struct Node {
T data;
Node *next;
Node *prev;
//Constructs Node Element
Node(T t, Node* p, Node* n) { data = (t); prev = (p); next = (n); }
// T *getNext() {return next;}
};
Node *head;
Node *tail;
public:
//Constructor
List() { head = NULL; tail=NULL; }
//Destructor
~List() {
while(head){
Node * temp(head);
head = head->next;
delete temp;
}
}
//is empty
bool empty() const {return (!head || !tail ); }
operator bool() const {return !empty(); }
//Push back
void push_back(T data) {
tail = new Node(data, tail, NULL);
if(tail->prev) //if the node in front of tail is initilized
tail->prev->next = tail;
if( empty() )
head = tail;
}
//Push front
void push_front(T data) {
head = new Node(data, NULL, head);
if(head->next)//if the node following head is initilized
head->next->prev = head;
if( empty() )
tail = head;
};
T pop_back() {
if( empty() )
throw("Error in List: List is empty\n");
Node* temp(tail);
T data(tail->data);
tail = tail->prev;
if( tail )
tail->next = NULL;
else
head = NULL;
delete temp;
return data;
}
T pop_front() {
if (empty())
throw("Error in List: List is empty\n");
Node* temp(head);
T data(head->data);
head = head->next;
if(head)
head->prev=NULL;
else
tail = NULL;
delete temp;
return data;
}
T getNext(){return next;}
};
#endif
getNext should be part of the struct Node and return a Node*
Node* getNext() { return next; }
Then from that you can get the value.
If you have to have it part of the list itself, which I would not recommend it will need to take a parameter of what Node you would like the next of:
Node* getNext(Node* n) {return n->next;}
Again, I recommend the first option.
Here is an approximate whole class with both of these:
template<typename T>
class List {
public:
struct Node {
Node* next, prev;
T data;
//some constructor and stuff
Node* Next() {return next;}
}
//some constructors and other functions
Node* getNext(Node* _n) {return _n->Next();}
}
then to use:
int main() {
List<int> l;
//add some stuff to the list
//get the head of the list
List<int>::Node* head = l.head; //or some corresponding function
//then
List<int>::Node* next = head->Next();
//or
List<int>::Node* next2 = l.getNext(head);
}
for starters getNext() should not return a pointer to the template class, it should return a pointer to the Node structure.
So it should be
Node* getNext(){return next;}
Because it's a member of Node struct and getNext is member of List. You should access it from an object of type Node.