I am working on an assignment that requires me to reimplement all of the Linked List functions. I am having some trouble understanding what the deconstructor does or what the code would be for it. I wrote a code that would deconstruct the list, but I don't think that is right (found below). I am also under the impression that I need to call the deconstructor in many of the LinkedList functions. Can someone please explain this to me and give me an idea of what the deconstructor code would look like?
~list()
{
for(int i=0; i<length; i++)
{
pop_front();
}
delete head;
}
Unless your code is visible, the exact solution cannot be suggested. You need to deallocate all the dynamically allocated linked list node if you are using your hand-written linked list. You can also use std::list.
The "destructor" should not be called explicitly, however it will be called as part of delete call. One way to destruct the 0 ending link list is as (example pseudo code):
class node {
int data;
node *next;
public:
~node()
{
delete this->next; // this will be chained until NULL is found
}
};
class list {
node *head;
...
public:
~list()
{
delete head;
}
};
And then call,
You have a memory leak as you are not doing delete of all the list nodes. A typical implementation could be like this:
For each node in the list
delete node;
Clear the entire list;
Also,
I am also under the impression that I need to call the deconstructor
in many of the LinkedList functions
No, you should not call the destructor explicitly from any of your functions. It is called automatically when either the object goes out of scope or when somebody does a delete of you list. This entirely depends on the code which is using your list class. However, note that you may want to delete a single node from the list in functions such as erase, but that doesn't mean that you have to call the destructor of list.
Related
1) So i have made a somewhat modified form of linked list that has indexed based addressing and other delete functions. I am just gonna copy the header file i made...
template<class T>
class LinkedList
{
public:
LinkedList();
~LinkedList();
int del_node(); // deletes the Node and element
int remove_node(); // deletes the Node only
int get_length();
int search_node(T*); // matches the pointer to see if its the same element
void add(T*);
void clear_list(); // calls deletes on all nodes and all elements
bool is_blank();
T& get_at(int); // operates like a vector get_at
private:
struct Node
{
T* element; // pointer passed to add(T*) is stored here.
Node* next;
}
Now see how i am adding an object in a linked list. I need to pass in an object pointer which i am passing in the form of
new Object()
This is particularly useful when i am adding Vertices of a graph. I just input the data and other fields from the user and call
LinkedList graph
graph.add(new Vertex(arguments));
Now there comes a situation when i have to copy some elements from the LinkedList A to B for temporary storage. Now i want to be able to remove elements from B after any kind of operation. But if i use delete it destroys the internal Node and deletes the object pointed by the pointer element i passed to it. So i created an additional function remove that only deletes the Node but not the object pointed by the element.
So i wanted to ask if its okay to do this or is there a design fault in my list and i should not be doing this? I am thinking of this from a library point of view for example if i would go about providing this class in a library. Is this suitable or will this confuse people? Any advice would be appreciated.
Please, I don't need any suggestions to use a replacement
function/class/library like vector. I am studying Data Structures and i have
to design any sort of data structure myself.
The more idiomatic fashion is to have Node::~Node always call delete element;, but add a T* Node::release();. This is what std::unique_ptr does for instance.
The implementation is straight forward:
T* Node::release()
{
T* tmp = element;
element = nullptr;
return tmp;
}
That way the Node d'tor is still correct, but you can "save" the data from deletion.
This is also the first step in addressing what I sense is a flaw in your implementation. You implement all functionality in LinkedList, even that which is relevant to the behavior of the internal class Node. Don't do that. Give Node a role and an interface related to that role. Than have LinkedList work by using that interface.
Ownership should be explicit when designing your class.
For that, you can use explicit method names and return std::unique_ptr when you are transfering ownership. With explicit method names you should be able to remove your comments.
template<class T>
class LinkedList
{
public:
LinkedList(const LinkedList&);
LinkedList(LinkedList&&);
LinkedList& operator=(const LinkedList&);
LinkedList& operator=(LinkedList&&);
void free_element(int); // deletes the Node and element
std::unique_ptr<T> extract_element(int); // deletes the Node only
int get_length() const;
void add_element(std::unique_ptr<T>);
void absorb_element(T*);
void free_all_elements(); // calls deletes on all nodes and all elements
};
I am currently writing a program in C++ that uses linked lists. The problem is, I can't really seem to figure out how to use linked lists. I know (A little) how to use classes though.
This is the LinkedList.h file given by the teacher (So I can't modify anything)
LinkedList.h
struct Node
{
void* data_;
Node* next_;
Node()
{
data_ = 0;
next_ = 0;
}
};
class LinkedList
{
private:
Node* first_;
Node* last_;
long listLen_;
public:
LinkedList();
~LinkedList();
void AddLinkToBack(void* ptr);
void* RemoveLinkFromFront();
Node* GetFirstNode();
long GetListLength();
};
I really just need advice on what to do for the Linked List function definitions but I will also listen to advice on improving my code. Thank you :)
Edit: Removed the other code in case someone in a future class stumbles upon this post.
I'm going to describe how to implement each function. Not going to give you the code.
LinkedList(): This is your constructor for the linked list. This function should be a part of the class where you have defined a linked list. It will be called whenever a linked list is initialised. You can use it define the first node of a linked list and set it to NULL.
~LinkedList(): This is your destructor for the linked list. It will deallocate all the memory allocated to the linked list. Implement it such that it deallocates memory for all elements of the linked list as opposed to just setting the first element to NULL.
AddLinkToBack(void* ptr): Add an element to the end of the linked list. This should traverse to the end of the linked list and set the next pointer of the last element to the pointer passed in the parameter. You should also dereference the void pointer before doing this.
RemoveLinkFromFront(): Remove the first element. To do this, you can set the first element of the list as first->next, and deallocate the memory given to the first element.
GetFirstElement(): Return a pointer to the first element of the linked list.
GetListLength(): Traverse the entire list and keep a pointer alongside which will increment every time you move to the next element. Return the counter when you reach the end of the linked list.
I have a uni assignment in which I have to implement a singly linked list that contains different objects that are derived from a common abstract base class called Shape.
I'll link to GitHub for the class implementation: shapes.h , shapes.cpp. So far it consists of Shape and its derived class Circle. There'll also be Rectangle, Point and Polygon later.
I should now implement a singly linked list of these different kinds of shapes. So far I've come up with the following class prototype for the List-class and the Node-class:
class Node
{
public:
Node() {}
friend class ShapeList;
private:
Shape* data;
Node* nextNode;
};
class ShapeList
{
public:
ShapeList(){head = NULL;}
void Append(Shape& inData);
private:
Node* head;
};
Adding elements void Append(Shape& inData) to a ShapeList-object should be able to be called from main in the following style:
ShapeList list1;
list1.Append( Circle(5,5,5) );
list1.Append( Rectangle( 4, 10, 2, 4) );
Given this information, how should I go about implementing void Append(Shape& inData)? I've tried several different approaches, but haven't come up with the correct solution so far.
It's also completely possible that the parameter to Append should be something else than (Shape& inData).
edit:
I've implemented Append(Shape& inData) but it works only sometimes:
Circle circle1;
ShapeList list1;
list1.Append( circle1 );
but not with
ShapeList list1;
list1.Append ( Circle(5,5,5) )
So far my Append()-implementation looks as follows:
void ShapeList::Append(Shape& inData)
{
//Create a new node
Node* newNode = new Node();
newNode->data=&inData;
newNode->nextNode=NULL;
//Create a temp pointer
Node *tmp = head;
if (tmp != NULL)
{
//Nodes already present in the list
//Traverse to the end of the list
while(tmp->nextNode != NULL)
tmp = tmp->nextNode;
tmp->nextNode=newNode;
}
else
head=newNode;
}
Does that look ok to you guys?
Since this is tagged under 'homework', I will only point you to the good direction. This may be too basic or maybe it is enough for your needs...
In a typical situation, you would simply use a container that is already written such as std::list.
But for implementing your own linked list
When you start from the head member of the ShapeList, you should be able to traverse the entire list and find a node for which 'nextNode' has never been assigned.
This is where you want to add a new node.
Now thee a a few tricks to be make things work:
1- In C++, variables are not automatically initialized. You must therefore initialize the many values when you create a new node, especially the next node pointer.
2- Instead of having pointers to references, I suggest that either you create copies of Shapes, of use some kind of smart pointers to avoid copying.
3- Don't forget about memory management, when you destroy your linked list, you will have to destroy all nodes individually since.
One very nice implementation of the singly linked list is as a circular list with the "head" pointer pointing at the tail. This makes it easy to insert at either the front or append to the end: in either case you create a fresh node, make the current tail point to it, and make it point to the current head, and then in the insert case make the head pointer point to the new node.
What you appear to be missing (other than what's already been pointed out: allocating, deallocating, and copying the nodes) is a way to know that you've actually created the list. So you'll want to add in some sort of output - either an operator << or a print() routine, which will walk the list, and call your graphical objects' printing mechanisms in order.
You say that it is possible that the argument to Append might not be Shape &data. Given the requirement of the calling convention specified, it should be:
Append( const Shape &data ) // provided shapes have copy constructors
{
Node *newNode = new Node( data ); // requires a constructor of Node that copies data to a freshly allocated location and sticks a pointer to that location in its data field - then Node's destructor needs to release that pointer.
... ( and the code to manipulate the existing list and newNode's next pointer )
}
Among other things this makes responsibility for management clear and simple.
If you have a Node constructor that takes both a pointer to a Node and a Shape, you should be able to do Append in two lines - one allocating the new Node and calling the constructor appropriately, and one modifying a pointer to point to the new node.
I would add - based on your edit - that you absolutely need to do the allocation and copy inside Append.
You probably want Node to be nested inside of ShapeList so its full name will be ShapeList::Node, not just ::Node.
Since Node will own some data remotely, you probably need to define the big three for it.
In line with that, when you push something onto the list, the list will hold a dynamically allocated copy, not the original object.
Edit: Append should take a Shape const & rather than a Shape &. A reference to const can bind to a temporary object, but a reference to non-const cannot, so the calls using parameters that create temporary objects (e.g., list.Append(Circle(5,5,5))) won't compile if the parameter is a reference to non-const object.
I'd also change Node::Node to require that you pass it a parameter or two. As-is, your linked-list code is dealing with the internals of a Node more than I'd like. I'd change it to something like:
Node::Node(Shape const *d, Node *n=NULL) : data(d), nextNode(n) {}
Then in append, instead of:
Node* newNode = new Node();
newNode->data=&inData;
newNode->nextNode=NULL;
You'd use something like:
Node *newNode = new Node(&inData); // or, probably, `... = new Node(inData.clone());`
...and Node's ctor would handle things from there.
Also note that it's easier to add to the beginning of a linked list than to the end (it saves you from walking the whole list). If you really want to add to the end, it's probably worthwhile to save a pointer to the last node you added, so you can go directly to the end, rather than walking the whole list every time.
Here is one way to handle the polymorphic requirement (std::shared_ptr), demonstrated with the STL singly linked list...
typedef forward_list<shared_ptr<Shape>> ShapeList;
ShapeList list1;
list1.push_back(make_shared<Circle>(5,5,5));
list1.push_back(make_shared<Rectangle>(4, 10, 2, 4));
Here is how it would effect Node:
class Node
{
public:
Node() {}
friend class ShapeList;
private:
shared_ptr<Shape> data;
Node* nextNode;
};
and ShapeList...
class ShapeList
{
public:
ShapeList(){head = NULL;}
void Append(const shared_ptr<Shape>& inData);
private:
Node* head;
};
The way I know how to represent a linked list is basically creating a Node class (more preferably a struct), and the creating the actual linkedList class. However, yesterday I was searching for the logic of reversing a singly linked list operation and almost 90% of the solutions I've encountered was including that the function, returning data type Node* . Thus I got confused since if you want to reverse a list no matter what operation you done, wouldn't it be in the type of linkedList again? Am I doing it the wrong way?
The linked list implementation I do all the time;
#include <iostream>
using namespace std;
struct Node
{
int data;
Node *next;
};
class linkedList
{
public:
Node* firstPtr;
Node* lastPtr;
linkedList()
{
firstPtr=lastPtr=NULL;
}
void insert(int value)
{
Node* newNode=new Node;
newNode->data=value;
if(firstPtr==NULL)
firstPtr=lastPtr=newNode;
else {
newNode->next=firstPtr;
firstPtr=newNode;
}
}
void print()
{
Node *temp=firstPtr;
while(temp!=NULL)
{
cout<<temp->data<<" ";
temp=temp->next;
}
}
};
You approach isn't wrong, but you might be giving too much emphasis on your linkedList class.
What does that class actually contain? A pointer to the first node, and a pointer to the last node (which is redundant information, since you can find the last node by only knowing the first one). So basically linkedList is just a helper class with no extra information.
The member functions from linkedList could easily be moved inside Node or made free functions that take a Node as parameter.
Well, what is a linked list but a pointer to the first node? A list is fully accessible provided you can get to the first node, and all you need for that is a pointer to the first node.
Unless you want to store extra control information about the list (such as its length for example), there's no need for a separate data type for the list itself.
Now some implementations (such as yours) may also store a pointer to the last node in the list for efficiency, allowing you to append an item in O(1) instead of O(n). But that's an extra feature for the list, not a requirement of lists in general.
Those functions might be returning of type Node* because after reversing the linked-list they will return the pointer to the First node of the list.
Suppose, I have a singly linked list and its basic building block is,
struct Node {
Data d;
Node *pNext;
// methods
~Node();
};
The head of the linked list is stored as,
Node *m_Head; // member of some class
When, I am done with the list, I will clean it by deleting each node as,
void Erase()
{
Node *pIter, *pTemp = m_Head;
while((pIter = pTemp) != 0)
{
pTemp = pIter->pNext;
delete pIter;
pIter = pTemp;
}
}
I thought, if I can simplify this. So I came up with an idea where I can clean this whole linked list with just a single instruction !
delete m_Head;
and destructor will look like:
Node::~Node() { delete this->pNext; }
Here my concern is, will it cause recursion (implicitly due to delete) ? If yes, then it's definitely a concern for bigger linked lists. Will compiler be able to help in any way for optimizing that ?
[Note: Not using any library facility like std::list or other.]
I think the question that you have to ask is, does each Node in the list own its pNext Node? If not, then it has no business deleting its pNext node in its destructor.
In most linked list implementations all the nodes are owned by the list, a node doesn't own all the nodes after it in the list. It makes more sense to keep the nodes as dumb (POD-structs) and let all of the logic reside in the list.
It's definitely a design "smell" that your node has a destructor but no copy constructor or copy assignment operator. I think this approach will cause more complexity when you come to code implementing insert, splice and erase single element functions as you will have to manually manage the pNext pointers in any case to avoid unintentional destruction of the entire tail of a list.
Of course: Only do this for learning purposes or when you are sure that your own List is really better for your use case
It depends. Possibly your compiler will detect a tail recursion and emit code that is conceptually equivalent to using a loop.
If not, then yes, it will recurse. Usually, some thousands of recursions should be possible on commodity boxes, if stack pressure is small (like in your case). However, there is no guarantee, and indeed, for really large lists, this can be a problem.
Also, I think that recursion is indeed not entirely appropriate for the concept of sibling nodes. A node hierarchy, like with quadtrees, cries for recursion, but I have a not so good time thinking in recursion (which forms a call hierarchy) when the list-concept is about sibling-nodes.
You may also consider the manual loop as a easy-to-achieve optimization over the recursion that will make your code more robust in a guaranteed way.
Btw, you could also should rip out the deletion of nodes into a holder class:
class List {
public:
~List() {
for-each-node
delete-node
}
private:
class Node {
Node *node_;
...
};
...
};
This is basically how the standard library's list is usually implemented. It makes the whole implementation easier to achieve and conceptually more correct (Nodes don't own their sibling logically)
Most compiler do tail call elimination in default setting. Some smarter one can convert non-tail calls to tail calls.
So, this method okay as long as you have some optimization turned on.
Raw pointers ? Manual calls to delete ?
Why not, simply:
struct Node {
Data d;
std::unique_ptr<Node> next;
};
Then you don't even have to worry about memory management at all, it's automatic!