I'm looking at the implementation of a singly linked list using unique_ptr on https://solarianprogrammer.com/2019/02/22/cpp-17-implementing-singly-linked-list-smart-pointers/. My question pertains to the following method:
3 struct List {
4 List() : head{nullptr} {};
5
6 // ...
7
8 void pop() {
9 if(head == nullptr) {
10 return;
11 }
12
13 std::unique_ptr<Node> temp = std::move(head);
14 head = std::move(temp->next);
15 }
16
17 // ...
18 };
I am wondering why the temporary is needed here? Why couldn't you simply do head = std::move(head->next)? Is this because it will result in a memory leak? When head is reassigned, does unique_ptr automatically free the current memory it's pointing to?
I was under the impression that smart pointers are fool proof from memory leaks. It seems in this case there might be a memory leak for the original head because there would no longer be a smart pointer pointing to it?
I am wondering why the temporary is needed here?
It is not really needed, but it is not bad to use, either.
Why couldn't you simply do head = std::move(head->next)? Is this because it will result in a memory leak?
You can. There will be no leak in this example.
When head is reassigned, does unique_ptr automatically free the current memory it's pointing to?
Yes. However, the old pointer will not be delete'd until after ownership of the new pointer is transferred first. Per cppreference:
https://en.cppreference.com/w/cpp/memory/unique_ptr/operator%3D
Transfers ownership from r to *this as if by calling reset(r.release()) followed by an assignment of get_deleter() from std::forward<E>(r.get_deleter()).
https://en.cppreference.com/w/cpp/memory/unique_ptr/reset
Replaces the managed object.
Given current_ptr, the pointer that was managed by *this, performs the following actions, in this order:
Saves a copy of the current pointer old_ptr = current_ptr
Overwrites the current pointer with the argument current_ptr = ptr
If the old pointer was non-empty, deletes the previously managed object
if(old_ptr) get_deleter()(old_ptr).
So:
In the case where temp is used, the pointer to the old node in head will first be moved to temp via its move constructor, resetting head to hold a nullptr. Then head.operator= will call next.release() and acquire that pointer. And then temp will go out of scope, delete'ing the old node.
In the case where temp is not used, head.operator= will call next.release(), save its old pointer and replace it with the released pointer, and then delete the saved pointer.
No leak either way.
I was under the impression that smart pointers are fool proof from memory leaks.
If used properly, yes.
It seems in this case there might be a memory leak for the original head because there would no longer be a smart pointer pointing to it?
There is no leak, as there is always a unique_ptr referring to the old node, until pop() exits and temp is destroyed, delete'ing the old node with it. Even if temp is omitted, the old node is still destroyed properly after ownership of its next pointer is transferred.
Related
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'm new to C++ and I found out that I have been spoiled by Java and newer programing languages like Swift. So I understand that sometimes you have to delete objects manually in C++ such as pointers. You can use the delete keyword or you can use the smart pointers. But I'm confused on whether that deletes the pointer itself, so the pointer can't be pointed again or if it deletes where the pointer is pointing.
I'm writing code that acts as a integer linked list. So I have a pointer pointing to the head and the tail of the list. When ever someone 'polls' something it should delete it from the list and then reset the head (or tail) accordingly. So I have some code that works fine for what I need it to do:
int IntegerLinkedList::pollFirst(){
if (tail == nullptr && head == nullptr) {
return 0;
} else if (head == tail) {
int ret = head->getData();
head = nullptr;
tail = nullptr;
//delete head;
//delete tail;
return ret;
} else {
IntegerNode* newHead = head->getNext();
head->setNext(nullptr);
newHead->setPrevious(nullptr);
int ret = head->getData();
//delete head;
head = newHead;
return ret;
}
}
But I never really delete the object that it was pointing at, I just remove all the pointers to it. Does that delete the object like it would in Java? Or do I have to manually delete it and how would I do that? Am I memory leaking??? Thanks so much
Also the code that updates the head is
void IntegerLinkedList::addFirst(int x){
IntegerNode* n = new IntegerNode(x);
if (head == nullptr && tail == nullptr) {
head = n;
tail = n;
} else {
head->setPrevious(n);
n->setNext(head);
head = n;
}
}
And the head was defined in the header as
IntegerNode* head;
Java has a garbage collector that essentially does what smart pointers do. That is, when the last reference to an object falls out of scope clean up the memory.
The delete keyword frees up the memory at a location the pointer points to. It doesn't actually do anything to the pointer variable itself (which is just an address).
The way to correct your code would be:
else if (head == tail) { // suppose head = 0xAD4C0080
int ret = head->getData();
delete tail; // cleans memory at 0xAD4C0080, head and tail are still 0xAD4C0080
head = nullptr; // now head = 0
tail = nullptr; // now tail = 0
return ret;
} else {
IntegerNode* newHead = head->getNext();
head->setNext(nullptr);
newHead->setPrevious(nullptr);
int ret = head->getData(); // obtain data while head still exists
delete head; // get rid of head
head = newHead; // remove stray reference to deleted memory
return ret;
}
If you attempted to use the value 0xAD4C0080 after calling delete in these examples you would get a segmentation fault.
As for smart pointers (quoting from your comment):
yes the smart pointers, but do they delete what it is pointing at or just themselves
Smart pointers delete what they are pointing to when they themselves get deconstructed (usually by falling out of scope). i.e.
void func() {
std::unique_ptr<int> pInt(new int);
} // pInt falls out of scope, std::unique_ptr<int>::~std::unique_ptr<int>() called
I'm confused on whether that deletes the pointer itself, so the
pointer can't be pointed again or if it deletes where the pointer is
pointing.
It deletes what the pointer is pointing to. The pointer must be pointing to an object that was allocated with dynamic scope (the new keyword).
I just remove all the pointers to it. Does that delete the object like
it would in Java?
Of course not.
Or do I have to manually delete it and how would I do that? Am I
memory leaking???
You have to manually delete it, using the delete keyword, and, yes, you are leaking memory unless you do that.
It is often said that it's easier to learn C++ from scratch, than to try to learn C++ if you already know Java. C++ objects work fundamentally differently than they work in Java.
And not only differently, but in ways that have no equivalent in Java, at all. There is no Java equivalent, for example, of an object instantiated in automatic scope, in C++.
The full explanation of various scopes of C++ class instances cannot be fully given in a brief answer on stackoverflow.com. You need to get a good book on C++, and start reading it.
And forget everything you know about classes from Java. C++ classes don't work that way. The longer you keep trying to draw analogies with Java, the longer it will take you to learn how to use C++ classes correctly.
Yes, some of the pain can be helped by using smart pointers, which will take care of some of the pain points. But understanding smart pointers in of themselves also requires complete understanding of C++'s class model. Smart pointers do not solve everything, and it is important to understand how C++ classes work, in order to understand what problems smart pointers do solve, and what problems they do not solve.
It depends on what the type is for head and tail. In C++, there are operators (e.g., operator=), and there are destructors. This can lead to certain interesting consequences. Take this example:
#include <memory>
int main() {
// allocate memory
int* ptr = new int;
// store in smart pointer
std::unique_ptr<int> smart_ptr(ptr);
// create another wrapped pointer
std::unique_ptr<int> smart_ptr2(new int);
// throw away location of allocated memory
ptr = nullptr;
// free memory by assigning to smart pointer
smart_ptr = nullptr;
// we can call delete safely here because ptr is null
delete ptr;
// data pointed to by smart_ptr2 will
// be deleted at end of scope
return 0;
}
Additionally, there are two operators for allocating memory, operator new and operator new[], which must be free'd using delete and delete[] respectively.
A more detailed explanation of these concepts may be beyond the scope of this site.
Am I memory leaking???
Yes, if you new something, you must delete somewhere. Assigning pointer to nullptr won't ever affect the allocated data.
But I'm confused on whether that deletes the pointer itself, so the pointer can't be pointed again or if it deletes where the pointer is pointing.
The delete keyword will delete the content at the address you send to it. It won't delete named variable, as variable on the stack are destructed at the end of the scope. Deleteting manually something with automatic storage led to undefined behaviour.
I see you are confused about std::unique_ptr No, they are not deleting themselves. Like any other variable, thier destructor are called and the variable is destructed. The thing about std::unique_ptr is that in their destructor, they delete the data they point to, with you having to delete it yourself. You should read about the RAII idom.
There's a thing about your code, these lines in particular:
head = nullptr;
tail = nullptr;
delete head;
delete tail;
This won't do a thing. You are deleting no data, as head and tail point to nothing. Since the delete keyword deletes the data pointer are pointing to, it won't delete anything.
However, take this example with std::unique_ptr:
{
std::unique_ptr<int> myPtr;
myPtr = std::make_unique<int>(); // or you can use `new int;`
myPtr = nullptr; // The old int you allocated with `std::make_unique` is deleted.
myPtr = std::make_unique<int>(); // assign a newly allocated int
}
// Here, myPtr will free his data as it leaves the scope.
If you use the new keyword, then you always need to use delete. Memory allocated using new is not managed.
Currently your program is leaking memory in the pollFirst method, because you do not free the memory that head and tail point to before re-assigning them. You can do this by calling delete head and delete tail before re-assignment.
You should look into using one of the smart pointer types, such as unique_ptr, if you want automatic management of the memory your pointers point to.
There are few things you should know about C++.
Destructor (DTOR) : Destructor is something which you define for your class. If you don't define a destructor of your own compiler does it for you.
When you call delete for a pointer the DTOR of that class is called which does the necessary cleanup. In order to test it, please put a breakpoint to the DTOR and run the code. Once the control hits DTOR, check the call stack, you will find out that last frame below the DTOR in call stack is the line where you call delete.
As far as the smart pointers are concerned, it does something similar to the garabage collector. Smart pointers have something called reference count. The moment reference count of a smart pointer goes to 0, the DTOR is called.
NOTE: It is advised to write your own DTOR if you have a data member in the class which is pointer.
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
If I remove a pointer to an object, what will be removed? Only the pointer or also the object which the pointer points to?
For example:
Assume I have a class with a variable
int *root
If I do the following in a method of that class
int *current = root;
delete current;
Will root also be deleted or only the pointer current?
I think you have a misconception about what delete does: delete deletes an object pointed to by a pointer that was previously allocated with new:
int* p = new int; // Create a new int and save its address in p
delete p; // Delete the int
Note that this does not delete p itself in any way, but only the object p points to! You can still use p like a normal variable, e.g. reassign it.
When you have multiple pointers to a pointee you only need to call delete on one of them.
int* root = new int;
int* current = root;
delete current;
std::cout << *root; // undefined behavior
The behavior of delete is described in ยง5.3.5:
6 If the value of the operand of the delete-expression is not a null
pointer value, the delete-expression will invoke the destructor (if
any) for the object or the elements of the array being deleted. In the
case of an array, the elements will be destroyed in order of
decreasing address (that is, in reverse order of the completion of
their constructor; see 12.6.2).
Yes, root is deleted, but depending of the compiler, current can still contain the address of an unexisting variable. So you have to do this to avoid mistakes :
delete current;
current = 0;
int *pointer = new int;
After this statement pointer would be pointing( pointer would be containing the address ) to a block of memory enough to store integer. What internally happens is some chunk from free store would be assigned allocated status.
When you execute
delete pointer;
That memory is returned back to free store so as to fulfill future needs. But you pointer would be containing the same address. When you execute delete again , it would led to undefined behavior since that block is already returned to free store ( that means you have lost the control over that memory through this pointer )
So, to be on safe side you generally set pointer to 0 after deleting that memory.
pointer = NULL;
In implementation of operator delete there is code which check if pointer is NULL, if it is then it returns. So, it's said that there's no harm in deleting NULL pointer.
I hope I have covered every basics.
In a linked list, I have the following code
Node *tmp = head;
Node *del = head->next;
tmp->next = del->next;
delete del;
del = tmp->next;
In my code, I deleted the pointer del first, later I point it to tmp->next. Will this work?
You don't actually delete the pointer variable itself, you just mark the memory area that it points to as free to use for future allocations (after calling the destructor but that's a different topic). The pointer variable just carries an address. Even after deletion the pointer variable is fine, but it points to an area in memory that has been freed, so its contents are undefined.
When the pointer variable is then reassigned to point to another memory address, it will have nothing to do with the old, "deleted" part of memory at all.