Deleting a nested struct with void pointers as members? - c++

I have the following class:
class Stack {
struct Link {
void* data;
Link* next;
void initialize(void* dat, Link* nxt);
}* head;
public:
void initialize();
void push(void* dat);
void* peek();
void* pop();
void cleanup();
};
The pop method is:
void* Stack::pop() {
if(head == 0) return 0;
void* result = head->data;
Link* oldHead = head;
head = head->next;
delete oldHead;
return result;
}
oldHead is a pointer to a struct Link, which has a void pointer as member. So by deleting oldHead I'm implicitly deleting that void pointer, right?
I'm reading Thinking in C++ by Bruce Eckel, and it says that deleting void pointers doesn't clean things up properly because delete needs to know the type of the pointer.
This code is implicitly deleting the void pointer data, so: Can someone explain why is this (implicit) way of deleting a void pointer different from deleting with delete <void pointer>?

Your terminology is causing ambiguity, but let me explain. Let's say you have:
struct foo
{
void* bar;
};
Whenever a foo ends its lifetime, bar simply stops existing too. So if you have:
{
foo f = { new int; }
}
You've leaked, as new int is never deleted. Likewise, when you do:
{
foo* f = new foo;
f->bar = new int;
delete f;
}
You've still leaked, since when delete f is run, you simply end the lifetime of what f is pointing to (just like what happened automatically above), ergo bar simply ceases to exist and the new int is not deleted.
To summarize, when an object's lifetimes ends, delete is not called on the members that are a pointer.
So when you call delete on a Link, it's the same situation as bar in foo above: you're deleteing the memory for a Link causing data to stop existing, but not actually deleting what it's pointing at.

By deleting Link, that void* memory space is not being deleted. You need to define a destructor which deletes the memory that has been allocated. Each new needs one delete. An example of this for the Link-struct would be to add a destructor that deletes data. If your assumption would be correct, then next would also be deleted, causing your entire linked-list to be deleted which would be a terrible behavior.
Calling delete on a pointer will call the destructor of the pointed to type. If that type has no destructor, no such destructor will be called. This is the case for a void pointer which doesn't have a destructor. In the case of inheritance, the destructor should always be virtual so that the deepest class in the hierarchy will have their destructor called. The memory will be correctly free'd even if you cast your pointers to the wrong types - it's just that the destructor will be called incorrectly.

"I'm implicitly deleting that void pointer, right?"
Well, you're deleting the pointer itself when you delete oldHead. You're not deleting or freeing its target, which is what you seem to be wanting, and is what happens when you call delete on a pointer.
(To see why this is the case, consider that you might define a struct with a void* pointer that points to something outside the struct. You wouldn't want the target to be freed just because the struct was deleted.)

One problem with deleting a void pointer happens when you are pointing to something with a destructor:
#include <iostream>
struct foo
{
~foo() { std::cout << "important work" << std::endl; }
};
int main()
{
foo *f = new foo;
void *v = f;
delete v;
}
If you run the code sample above, you will see that the destructor is never called.

Related

why the raw pointer get by std::unique_ptr's get() can not delete the object and how is that implemented

As the following code presents, I tried to delete the object by the raw pointer get from a unique_ptr. But, as the output shows, the complier reported errors. However, for raw pointers, we can do this int *p1 = new int{100}; int* p2 = p1; delete p2;.
Besides, I thought that unique_ptr maintain its ownership by move semantics. As the get() function of unique_ptr returns the raw pointer, how does the unique_ptr can still have the ownership of the object. Meanwhile, why the raw pointer doesn't get the ownership. At the same time, I am confusing how does this implemented. Thanks.
#include <iostream>
#include <memory>
int main(int argc, char const *argv[])
{
std::unique_ptr<int> newPtr = std::make_unique<int>(1234);
std::cout << *newPtr << std::endl;
int* rawInt = newPtr.get();
*rawInt = 200;
delete rawInt;
rawInt = nullptr;
std::cout << *newPtr << std::endl;
return 0;
}
The code was performed on MacOs and the output is:
std::unique_ptr is a really simple class. Conceptually, it's basically just this:
template <typename T>
class unique_ptr
{
private:
T* ptr;
public:
unique_ptr(T* p) ptr{p} {}
~unique_ptr() { delete ptr; }
T* get() { return ptr; }
T* release() {
T* p = ptr;
ptr = nullptr;
return p;
}
// other stuff
};
It just has a pointer member, and deletes the pointed-to object in its destructor. In reality there's a bit more to it, but that's essentially it.
The get member just returns a copy of the unique_ptr's managed pointer. That's it. Since the unique_ptr's pointer member is still pointing to that object, its destructor will still delete the object. If you also delete that object via another pointer to it then it will get deleted twice, which results in undefined behavior.
The release member function, on the other hand, sets the unique_ptr's pointer member to nullptr before returning a copy of its original value. Since its member pointer is null, its destructor won't delete anything.

how delete class variable in struct?

InitMyObject(MyObject* ptr)
{
*ptr = MyObject();
}
struct Data
{
MyObject obj;
};
extern Data data;
// ...
InitMyObject(&data.obj);
delete &data.obj; // ? is this ok
How I can delete (call deconstructor) data.obj, I also try Data::obj as pointer (nullptr default) then pass the pointer but crashed on Init.
How I can delete (call deconstructor) data.obj
The destructor of data will destroy its subobjects. Since data has static storage, it is automatically destroyed at the end of the program.
delete &data.obj; // ? is this ok
No. You may only delete the result of a non-placement new expression. You may not delete a pointer to a member object.

freeing a void pointer

How to free a void pointer.
struct vStruct {
void *vPtr;
struct vStruct *next;
};
struct vStruct sObj;
struct vStruct *sObjNew = sObj;
delete sObjNew->vPtr; -----------> Is this correct way to delete void pointer
delete sObjNew;
Showing error Operator 'delete', applied to void* argument having has undefined behavior, and most likely does not invoke the object's destructor.
You do not delete a void pointer. Why? Because:
'delete', applied to void* argument has undefined behavior, and most likely does not invoke the object's destructor.
How would your compiler know which type the pointee object has? And therefore which destructor to call? (Though it can probably determine how much memory to free, depending on the allocation mechanism.)
Do not store a void* — use a "real" pointer type instead. If you need to "hide" the true type, consider employing polymorphism instead of antiquated C practices.
You should not delete a void pointer. delete works for specific types (such that compiler knows, which destructor should be called - as stated in error message).
If you want to hold unspecified type in your structure, you have to wrap it somehow.
class DataWrapper
{
public:
virtual void * GetData() = 0;
virtual ~DataWrapper()
{
}
};
class MyDataWrapper
{
protected:
MyData * data;
public:
MyDataWrapper(MyData * newData)
{
data = newData;
}
void * GetData()
{
return data;
}
~MyDataWrapper()
{
delete data;
}
};
struct vStruct
{
MyDataWrapper * vPtr;
struct vStruct *next;
~vStruct()
{
if (vPtr != null)
delete vPtr;
}
};
vStruct sObj;
sObj.vPtr = new MyDataWrapper(new MyData());
// When sObj gets deleted, MyDataWrapper is
// deleted too (thanks to the ~vStruct) and
// in effect, the allocated data is deleted too.
Note, that it's a simple example, it can be written more aesthetically.
How was vPtr initialised?
If it points to data the struct doesn't own, you must not destroy it.
If it points to data created using malloc, you should call free.
If it points to data created using new, you'd need to cast it to the correct (or a compatible) type before calling delete to allow the correct destructor to be called.
Note that your example code won't compile but suggests vPtr is not being initialised at all. You must initialise vPtr in all vStruct instances you create. Attempting to free an uninitialised vPtr would have undefined consequences but would likely crash.

Why isn't this a memory leak? Or is it? Deleting base class pointer without virtual destructors

I've been meddling around, testing for memory leaks with Intel Inspector, when I noticed something that should not be. I inherit from std::vector which is not supposed to have a virtual destructor, I have an extra member in the derived class, I do dynamic memory allocation on it, in main I create a derived class on the heap, cast to base class, call delete... and no memory leak is detected??? By all logic, I should get a memory leak.
template <typename T>
class DynamicArray : public std::vector<T> {
public:
DynamicArray() : children(nullptr) {
children = new int(50);
}
~DynamicArray() {
if (children) delete children;
}
DynamicArray& operator<<(const T& value)
{
push_back(value);
return *this;
}
private:
int *children;
};
int main() {
DynamicArray<int> *pArray = new DynamicArray<int>;
(*pArray) << 4 << 5;
static_cast<std::vector<int>*>(pArray);
delete pArray;
}
pArray is still of type DynamicArray<int> and wiil call the right destructor, this would likely leak:
std::vector<int>* wrong = static_cast<std::vector<int>*>(pArray);
delete wrong;
edit: as Ben Voigt correctly mentioned, this last code snippet actually undefined behavior since the destructor of std::vector is not virtual. So it is not even guaranteed that this will leak
This expression has no side-effects:
static_cast<std::vector<int>*>(pArray);
In your code, the delete and new actually match perfectly.
Also, this line allocates ONE int, so you may not notice it in your memory analysis:
children = new int(50);

Field variable deleted before actual destructor called?

What would make a field variable become obsolete before entering the destructor upon deletion of the object?
I was a looking for an answer for this problem I'm having on this site and came across this:
Lifetime of object is over before destructor is called?
Something doesn't add up at all: if I've declared a pointer to SomeClass inside another WrapperClass, when I construct the WrapperClass I need to create a new SomeClass and delete it on destruction of the wrapper.
That makes sense and has worked so far.
The pointer is still valid and correct well into the destructor otherwise obviously I wouldn't be able to delete it.
Now my problem is that my field members (both an int and a pointer to a SomeClass array) of WrapperClass are garbage when I call the destructor. I've checked the wrapper object just after construction and the data is fine. The wrapper is actually a pointer in another Main class and the problem occurs when I destruct that Main (which destructs the wrapper) but works fine if I just delete the wrapper from another method in Main.
My paranoia led me to the above mentioned answer and now I'm totally confused.
Anybody care to shed some light on what's really going on here?
EDIT:
Node is the SomeClass.
class WrapperException{};
class Wrapper {
private:
struct Node { /*....*/ };
int numNodes;
Node** nodes;
public:
Wrapper() : numNodes(0) { nodes = new Node*[SIZE]; }
Wrapper(const Wrapper& other) { throw WrapperException(); }
Wrapper& operator=(const Wrapper& other) { throw WrapperException(); }
~Wrapper() { //calling delete Main gets me here with garbage for numNodes and nodes
for(int i = 0; i < numNodes; i++)
delete nodes[i];
delete nodes;
}
};
class MainException{};
class Main {
public:
Main() { wrapper = new Wrapper(); }
Main(const Main& other) { throw MainException(); }
Main& operator=(const Main& other) { throw MainException(); }
~Main() { delete wrapper; }
private:
Wrapper* wrapper;
};
You need to use the Standard library to implement this behaviour.
class Wrapper {
private:
struct Node { /*....*/ };
int numNodes;
std::vector<std::unique_ptr<Node>> nodes;
public:
Wrapper() : numNodes(0) { nodes.resize(SIZE); }
// No explicit destructor required
// Correct copy semantics also implemented automatically
};
class Main {
public:
Main() : wrapper(new Wrapper()) {}
// Again, no explicit destructor required
// Copying banned for move-only class, so compiler tells you
// if you try to copy it when you can't.
private:
std::unique_ptr<Wrapper> wrapper;
};
This code is guaranteed to execute correctly. When in C++, if you have used new[], delete or delete[], then immediately refactor your code to remove them, and review three times any use of non-placement new- constructing a unique_ptr is pretty much the only valid case. This is nothing but a common, expected outcome of manual memory management.
Since Grizzly isn't answering, I'll put this out there.
Both your Main class and your Wrapper class need properly implemented copy constructors and assignment operators. See The Rule of 3.
The problem is, if your class ever gets copied(which is easy to happen without you even realizing it), then the pointers get copied. Now you've got two objects pointing to the same place. When one of them goes out of scope, it's destructor gets called, which calls delete on that pointer, and the pointed to object gets destroyed. Then the other object is left with a dangling pointer. When it gets destroyed, it tries to call delete again on that pointer.
The lifetime of your wrapper object has ended, but the integer and pointer sub-objects as well as the pointee are still alive. When you invoke delete on the pointer, the pointee's lifetime ends, but the pointer still remains alive. The pointer's lifetime ends after your dtor is complete.
Thus, if your members have become corrupted, there is something else afoot.
Node** nodes;
should be
Node * nodes;
Also the destructor is wrong. It should be:
for(int i = 0; i < numNodes; i++)
delete nodes[i];
delete [] nodes;
There might be other problems as well as e.g. you haven't created a copy constructor or assignment operator so that might make it so that the copy of an object then deletes the object for you.
EDIT: changed the destructor...