Assume we have a List class with a data member called front which is a Node pointer. Below is a member function to display the members of a list (the calling object).
It has an error. Identify and describe the error in a few
words. Then correct the error.
void display ()
{
Node *temp= new Node;
temp=front;
while (temp!=NULL)
{
cout<<temp->data<<"\t";
temp=temp->next;
}
}
This was one of the question in my test and I couldn't find anything wrong in the above given code. I even ran this code in the compiler and it works fine.
Can anyone tell where the error is?
Node *temp = new Node; // a pointer, called temp, allocated to a new block
//of memory of size Node
temp = front // the pointer to the block of memory was just overwritten, and now you
//don't have a pointer to the block of memory you just allocated
First, you don't need to call new here. Just declare the pointer and assign it at the same time.
Second, since you did call new, you've just created a memory leak, that is, you've allocated memory that now has no way of being deallocated (until the program shuts down).
Third, you should access front with an accessor method.
myList.GetFront() //this should return a pointer to the front of the list
Why? Well, what happens if you accidentally do something like:
front = front->next;
You've just lost the pointer to the front of your list, and so has every other method that uses front.
Related
So when we actually create the node object, why does it have to be a pointer. Why cant be just make it a regular Node object, use . for the data and then use the arrow operator for next node.
class Node {
int data;
Node * next;
};
int main() {
Node * node1, node2; // why make it a pointer
Node node3, node4; // Why dont people leave it has non pointer,
because you already have Node* next in the class.
}
Pointers play well with dynamic allocation. You can continue using the object through pointers until you purposefully free it. Automatic objects on the other hand, get destroyed at the end of the scope leaving all pointers (and references!1) to them dangling.
If you know that you won't use any of the pointers outside the scope that contains the object itself (this is likely true for your example in main()), then go right ahead and use an automatic object.
But one final complication is that if one of the pointers is a "smart" pointer that knows how to free the object it points to, you have to create the attached object using the matching allocation function. Attempting to free an automatic (scoped)2 object will result only in misery.
1 The oft-repeated statements like "a reference is equivalent to the object itself" and "a well-defined program cannot create an invalid reference" are horribly wrong but that's too long a discussion to have here.
2 Misery also accompanies trying to free an object which is static or a member subobject or dynamically allocated using a different allocator.
You can do this for some educational purpose.
class Node {
public:
int data;
Node * next;
Node(int d, Node *nextNode = NULL) :data(d), next(nextNode) {}
Node() {}
};
int main() {
Node node3, node4(20);
node3.data = 10;
node3.next = &node4;
std::cout << node3.data << " " << node3.next->data << std::endl;
}
But in real world, in creation of linked linked, you don't know when you have to create a list. In your case its a small class, but consider if it a big class with several complex data structure. If you go in this way, unnecessarily you will create a object on stack.
It will also create ambiguity while deleting memory, you will have to take a special care. In fact, you will not be able to remove memory for your first node until execution goes out of memory.
now here is one big issue, If you are passing this list to some other function(may be creating a list in a function and returning it to caller), this first Node will go out of scope and the memory allocated to your list will be leaked.
Hope I cleared your doubt.
Try to change it into a Node object and compile the code. There should be a compilation error saying that it is an incomplete type. This is because the next field is creating an infinite loop and the compiler cannot calculate how big that field is supposed to be.
A pointer is a reference to a place in memory, since each node should reference the other node and not store its value directly, because if it would store a value you need to update it each time the other node is updated.
One can also use a pointer to allocate memory in the "heap" with the "new" keyword(dynamic storage), otherwise it could be stored in the "stack"(automatic storage), which has a very limited memory(you can get a stack overflow).
In a linked list that uses
struct Node {
T value;
Node* next;
}
For every used new operator (new Node()), there must be a delete operator.
An example of a destructor for such a list
~LinkedList(){
Node* tmp = head;
while(tmp! = nullptr){
delete tmp;
tmp = tmp->next;
}
}
My question is, what exactly gets "deleted" that allows me to use next pointer
even after delete is used? Does it only delete the value? How does that actually look in the memory?
In C++ when you delete an object on the heap nothing actually gets cleaned up, it just marks the memory as "free". This means that another call to new or malloc may overwrite that memory.
Accessing a deleted pointer is undefined behaviour because their are no guarantees on the data that resides there. I'm not very well versed in how the OS handles memory but I believe it could even be the case that your program no longer owns that page if that was the last item you deleted from that section of memory. If this happened to be the case then dereferencing that pointer will cause a segmentation fault on most desktop OS's.
If you wanted to safely move the head you should assign a temporary value to the next item while the pointer is alive, then you can delete the underlying object from memory.
When a data is deleted the pointer becomes undefined and for sure does not reach to memory it was pointing before, therefore there is no way to call tmp=tmp->next' after deletion.
The proper destructor declaration would be:
~LinkedList()
{
while (head != nullptr)
{
Node * tmp = head->next;
delete head;
head = tmp;
}
}
BTW.: Please read some good books on how to implement lists.
BTW.2: Use some standard containers like vector or list if you really need it.
I was implementing a LinkedList using C++, and I seem to have forgotten a few things when dealing with dynamically allocated memory.
I have a node class:
class Node {
public:
Node(int d) {
data = d;
next = NULL;
}
Node(int d, Node* n) {
data = d;
next = n;
}
int data;
Node* next;
};
and in my LinkedList class, I have the following method:
void remove(int n) {
Node* current;
current = head;
Node* previous = NULL;
while ( current->data != n && current->next != NULL) {
previous = current;
current = current->next;
}
if (current->data == n) {
previous->next = current->next;
current->next = NULL;
delete current;
}
else {
std::cout << "Node not found" << std::endl;
}
}
I seem to have forgotten..When I do delete current does that delete the Node ? Like the actual object that the pointer current points to? Or does it just delete the pointer? Or does the deletion of a pointer pointing to dynamically allocated memory using delete delete both the pointer and the object? Or do I need to have defined a Node class destructor for that?
It just deletes the struct -in your case node- it points to, you can still use that pointer -make it point to another node-, in fact there's no way delete the pointer itself since it's allocated on the stack. it's automatically "deleted" when you leave the function.
p.s: no need to set current->next to null
Delete just free's the memory pointed to. This has the following implications:
You are not allowed to access the memory at this location (use after free)
The amount of memory you needed for your Node object is free, meaning your program would use less RAM.
The pointer itself points either to a non-valid location or NULL, if you follow best practise and set it to NULL manually.
The data at the memory location where your object was can be overwritten by any other task that has a valid pointer on this location. So technically the Node data still remains in memory as long as nobody else overwrites it.
delete p causes the object pointed to by p to cease to exist. This means that
1, If the object has a destructor, it is called; and
2. p becomes an invalid pointer, so that any attempt to dereference it is undefined behaviour.
Generally, the memory occupied by said object becomes available to the program again, though this is really an implementation detail.
The phrase "delete the pointer" is normally a sloppy shorthand for "delete the object pointed-to by the pointer".
Assuming that you have allocated your object using new delete on a pointer does the following:
calls the destructor of the object
request that the memory is free ( when that happens is actually implementation dependent )
At some point the memory manager will free and mark it as non-accessible by the process.
Thus it is up to you to set the pointer after calling delete to an agreed value. The best practice it to set it as nullptr for the latest compilers.
It does delete the actual structure pointed to by current. Pointers remain intact. No need for defining destructor.
The delete operator is to be applied to pointer to object. The pointer is an address of memory on heap allocated by calling new. Internally there is just table of addresses allocated by new. So the key to free such memory is just that address. In your case such address is stored in variable of type pointer to Node named current.
There are few problems in your code. The problematic one is that you have no posibility to tell whether node stored in current is actually allocated on heap. It might happen that current node is allocated on stack. E.g.
void someFunction(LinkedList &list) {
Node myLocalNode(10);
list.add(&myLocalNode);
list.remove(10); //<-- disaster happens here
}
The same applies to statically allocated global variables.
You must take care of extreme cases. Think about what happens when deleted object is the first one, pointed by variable head. By deleteing its memory you end up with dangling pointer in head, pointing to either unallocated memory or memory used by someone else.
My third objection is to writing such structure at all. I hope it is just some school excercise, because in any other cases you should (almost must) use some existing list like std::list from C++ STL.
Whenever you call delete on a pointer variable, the object to which it is pointing to gets deleted from the memory, however the 4 bytes allocated to the actual pointer variable (in your case, the current variable), the 4 bytes will be freed only when the variable will go out of scope, ie At the end of the function
I have this small piece of code in my IntList implementation to push_back nodes. My question is if the *pnode cause memory leak or do I need to delete it at the end.
void IntList::push_back(int data){
if (first){
IntNode *pNode = first;
while(pNode->next!=0){pNode = pNode->next;}
pNode->next = new IntNode(data);
} else
first = new IntNode(data);
}
No you don't need to call delete on pNode. You only call delete on things created with new. With the code as it is now pNode is a stack object and will automatically be destroyed when it goes out of scope at the end of the function.
You don't need delete pNode. Moreover, you can't do it in this particular case.
After you create something with new you must delete it exactly one time - once you'll never use it.
After removing the object with delete, attempt to read its contents is an undefined behavior. The pointers are generally one of the most bug generating part of the c++, so it is good that you are trying to understand it better.
You can visualize yourself this way: You can buy a house (house is a piece of memory) with new. It returns you address if it. It is your house now and can do with it what you want. You can also sell it with delete. Until this is done, you can give your friend your home address so that they can come to you. Distribution of the address, causing copying it (e.g. IntNode *pNode = first;). However, you still have only one house. So no matter how many times you copy your home address, you can sell the house only once.
I would advise using smart pointers (e.g. std::unique_ptr), but I think this program is for learning programing, so don't do it ;)
You have to delete the IntNodes created with new eventually, likely in the destructor of the container and the pop_back function. pNode itself (the pointer) was allocated on the stack, and not with new, so does not need to be deleted.
You need delete nodes on each function that removes nodes from the list, not when inserting. Not doing so would cause a leak.
If there are still allocated nodes when you destruct the object you need to iterate the whole list to remove all the nodes too. Not doing so would cause a leak too.
I suppose that this is a college assignment, as there are millions of battle-tested linked list implementations out there.
You'd do better if you maintain a tail node up to date on all functions, so you could avoid iterating the whole list to insert a node on the tail.
The code you show is not sufficient to tell if you're leaking memory or not. You show the routine responsible for allocation, but not the code where you're performing deallocation.
If you don't have any code performing deallocation, then yes, obviously that leaks. C++ does not perform automatic garbage collection as you may be used to in some other languages.
You are using naked new, so even if you do have some other code attempting to do deallocation, there's a good change it's being done incorrectly.
In C++ you generally shouldn't be using the new operator directly, and you should learn to use RAII to handle resources instead. IntList presumably uses owning raw pointers, which is another thing to be avoided. In this case you should probably be using unique_ptr<IntNode>. For example:
struct IntList {
struct IntNode {
unique_ptr<IntNode> next;
int data;
IntNode(int data) : data(data) {}
};
unique_ptr<IntNode> first;
// returns a reference to the null pointer which terminates the linked list
unique_ptr<IntNode> &get_tail() {
if (!first) {
return first;
}
IntNode *pNode = first.get(); // pNode is a non-owning pointer
while (pNode->next) {
pNode = pNode->next.get();
}
return pNode->next;
}
void push_back(int data) {
get_tail() = make_unique<IntNode>(data);
}
};
There actually is a problem with the above code, but the issue is unrelated to memory leaks. When the list is destroyed, first is automatically destroyed, which automatically destroys first->next, and so on. It's possible to insert so many elements into the list that this chain of destruction 'smashes' the function stack, leading to undefined behavior.
The problem can be fixed with an IntList destructor that destroys the nodes in the opposite order:
IntList::~IntList() {
while (first) {
unique_ptr<IntNode> tmp = std::move(first);
first = std::move(tmp->next);
}
}
It's interesting that this is an example of an exception to the Rule of Three.
I'm not sure if this question has been asked before ( searched through SOF and couldn't find an answer)
I wrote a LinkedList class and a function to reverse it.The function as follows,
struct LinkedList::element* LinkedList::recurrsiveReverseList(element* head){
element* tempList;
if(head->next == NULL){
return head;
}else{
tempList = recurrsiveReverseList(head->next);
head->next->next = head;
head->next = NULL;
return tempList;
}
}
here I am declaring a local pointer variable and making some changes to it and returning it back to the caller. In C++, when I declare a local variable inside a function the scope exists only inside the function. Now when I return the pointer from the function how does it work? I am able to understand the logic and get the result (luckily) but I am not able to completely understand the working here.
Can somebody clear my doubt?
The scope of tempList terminates when you exit the function but tempList is a pointer to a block of memory whose scope does not terminate there because it's been undoubtedly allocated by new. Memory allocated in such a way is valid right up until the point you delete it, regardless of how many functions you go in to or out of.
By passing the pointer back to the caller, it preserves said pointer elsewhere, where you can use it.
A simple example:
static char *fn (void) {
char *rv = new char[42];
return rv;
}
int main (void) {
char *x = fn();
delete [] x;
return 0;
}
In the code above, the scope of rv is limited to the fn function after it's declared.
The scope of x is limited to the main function after it's declared.
However the memory allocated by new comes into existence within fn and continues to exist after returning to main. The address of said memory, initially stored in rv, is transferred to x by the assignment of the fn return value to x.
Not sure if someone else explained it this way, but the pointer itself is nothing more than a number, like... 0x12345678. That number in turn addresses a position in the memory of a computer that contains the actual value you are looking for, which is the linked list node.
So when you return that address, it's okay that the original variable was destroyed. Like copying down a street address to a different piece of paper, then throwing away the original paper. The house that is at the address you have is still there.
The pointer object tempList ceases to exist when you leave the function. But that's ok; you're returning (a copy of) the value that was stored in the object, not the object itself. It's just like
int n = 42;
return n;
(Returning the address of a local variable is what gets you into trouble.)
I assume your linked list nodes are allocated on the free store (i.e. with new).
When you create an object with new, it is created on the free store and exists until you call delete on it. You can make as many pointers to that location as you want, and it exists independent of any function calls it may have been made in. So in this function, you are just returning a pointer by value to that location on the free store. The pointer is just a number which is the address of the object, like returning an int by value.
tl;dr: You obviously know you can return local objects by value because a copy is made. In this function, it returns a copy of the pointer which points to a location on the free store which is only destroyed when delete is called with a pointer to that memory location.
As another note, you probably should not return a pointer to the new head of the list but rather take a pointer to the head of the list by reference and change the list through that, so if someone forgets to assign their old head pointer to the one returned by recurrsiveReverseList, things aren't messed up.
The scope of usage of tempList variable is limited within the method as its local, but it holds a memory address which is what is returned.
The code that call its will receive this memory address, not the variable tempList.