Recursive Destructors in C++ - c++

Suppose I have a class similar to the following:
#include <vector>
class element
{
public:
element();
~element();
virtual void my_func();
private:
std::vector<element*> _elements;
};
How would I go about implementing the destructor?
I was thinking something like this, but I'm not sure. I am worried about memory leaks since I'm relatively new to C++.
void element::destroy()
{
for(int i = 0; i < _elements.size(); ++i)
_elements.at(i)->destroy();
if(this != NULL)
delete this;
}
element::~element()
{
destroy();
}
Thank you.
PS:
Here is a sample main:
int main()
{
element* el_1 = new element();
element* el_2 = new element();
element* el_3 = new element();
el_1.add_element(el_2);
el_2.add_element(el_3);
return 0;
}
Also, what if I do this (aka don't use new):
int main()
{
element el_1;
element el_2;
element el_3;
el_1.add_element(&el_2);
el_2.add_element(&el_3);
return 0;
}

element::~element()
{
typedef std::vector<element*>::const_iterator iterator;
for (iterator it(_elements.begin()); it != _elements.end(); ++it)
delete *it;
}
delete this in a destructor is always wrong: this is already being destroyed!
In addition, you would need to declare a copy constructor and copy assignment operator (either leaving them undefined, making your class noncopyable, or providing a suitable definition that copies the tree).
Alternatively (and preferably), you should use a container of smart pointers for _elements. E.g.,
std::vector<std::unique_ptr<element>> _elements;
When an element is destroyed, its _elements container will be automatically destroyed. When the container is destroyed, it will destroy each of its elements. A std::unique_ptr owns the object to which it points, and when the std::unique_ptr is destroyed, it will destroy the element to which it points.
By using std::vector<std::unique_ptr<element>> here, you don't need to provide your own destructor, because all of this built-in functionality takes care of the cleanup for you.
If you want to be able to copy an element tree, you'll still need to provide your own copy constructor and copy assignment operator that clone the tree. However, if you don't need the tree to be copyable, you don't need to declare the copy operations like you do if you manage the memory yourself: the std::unique_ptr container is itself not copyable, so its presence as a member variable will suppress the implicitly generated copy operations.

class element
{
public:
element();
~element();
private:
std::vector<element*> _elements;
};
Per the Rule of Three, this lacks the definition of copy constructor and assignment operator.
element::~element()
{
destroy();
}
Calling delete this (which is what destroy() does) from a destructor is always wrong, because the destructor is what is called when the current object is being deleted.
for(int i = 0; i < _elements.size(); ++i)
_elements.at(i)->destroy();
While this does the job of calling delete on the objects you call destroy() for, this arrangement has the disadvantage that you must not destroy such objects other than through calling destroy(). In particular, this
element el_1;
will be a problem, because e1_1 will be automatically destroyed when it falls out of scope bay calling its destructor. Since this calls destroy(), which invokes delete this on an object not allocated using new, this causes Undefined Behavior. (If you are lucky it blows up into your face right away.)
It would be far better to remove the delete this from the destructor, and only have the destructor delete the objects in the object's vector:
for(std::vector<element*>::size_type i = 0; i < _elements.size(); ++i)
delete _elements[i];
if(this != NULL)
delete this;
Checking a pointer for NULLness is never necessary, because delete is defined to do nothing when the pointer passed to it is NULL.

The delete this shouldn't be there, as the object is already under destruction by definition.
If you copy an element then all the pointers within its member vector are copied too; then when the original goes out of scope their pointees are destroyed, and the element copy has a vector of dangling pointers. You need a copy ctor and assignment operator.
So, just this basic start has already created two very serious faults. This is a sign that the design is to be avoided. It doesn't appear to be all that clear what the ownership semantics are to the programmer who uses it.
Why is dynamic allocation required here at all? Why not store elements objects themselves?
#include <vector>
class element
{
public:
void add_element(element const& e) {
_elements.push_back(e);
}
private:
std::vector<element> _elements;
};
This will change your program subtly if you previously had multiple references to the same element going into different other elements, but you'll have to decide for yourself whether that tangled mess of dependencies is something you need.

Nope, it doesn't work that way. You never delete this. You just don't. Basically, take out your destroy method, put everything into ~element() except for the delete this part. And instead of calling the destroy method, just delete _elements[i];.

Related

C++ class destructor not being called automatically causes memory leak?

(Before anyone asks: no, i didn't forget the delete[] statement)
I was fiddling around with dinamically allocated memory and i run into this issue. I think the best way to explain it is to show you these two pieces of code I wrote. They are very similar, but in one of them my class destructor doesn't get called.
// memleak.cpp
#include <vector>
using namespace std;
class Leak {
vector<int*> list;
public:
void init() {
for (int i = 0; i < 10; i++) {
list.push_back(new int[2] {i, i});
}
}
Leak() = default;
~Leak() {
for (auto &i : list) {
delete[] i;
}
}
};
int main() {
Leak leak;
while (true) {
// I tried explicitly calling the destructor as well,
// but this somehow causes the same memory to be deleted twice
// and segfaults
// leak.~Leak();
leak = Leak();
leak.init();
}
}
// noleak.cpp
#include <vector>
using namespace std;
class Leak {
vector<int*> list;
public:
Leak() {
for (int i = 0; i < 10; i++) {
list.push_back(new int[2] {i, i});
}
};
~Leak() {
for (auto &i : list) {
delete[] i;
}
}
};
int main() {
Leak leak;
while (true) {
leak = Leak();
}
}
I compiled them both with g++ filename.cpp --std=c++14 && ./a.out and used top to check memory usage.
As you can see, the only difference is that, in memleak.cpp, the class constructor doesn't do anything and there is an init() function that does its job. However, if you try this out, you will see that this somehow interferes with the destructor being called and causes a memory leak.
Am i missing something obvious? Thanks in advance.
Also, before anyone suggests not using dinamically allocated memory: I knwow that it isn't always a good practice and so on, but the main thing I'm interested in right now is understanding why my code doesn't work as expected.
This is constructing a temporary object and then assigning it. Since you didn't write an assignment operator, you get a default one. The default one just copies the vector list.
In the first code you have:
Create a temporary Leak object. It has no pointers in its vector.
Assign the temporary object to the leak object. This copies the vector (overwriting the old one)
Delete the temporary object, this deletes 0 pointers since its vector is empty.
Allocate a bunch of memory and store the pointers in the vector.
Repeat.
In the second code you have:
Create a temporary Leak object. Allocate some memory and store the pointers in its vector.
Assign the temporary object to the leak object. This copies the vector (overwriting the old one)
Delete the temporary object, this deletes the 10 pointers in the temporary object's vector.
Repeat.
Note that after leak = Leak(); the same pointers that were in the temporary object's vector are also in leak's vector. Even if they were deleted.
To fix this, you should write an operator = for your class. The rule of 3 is a way to remember that if you have a destructor, you usually need to also write a copy constructor and copy assignment operator. (Since C++11 you can optionally also write a move constructor and move assignment operator, making it the rule of 5)
Your assignment operator would delete the pointers in the vector, clear the vector, allocate new memory to hold the int values from the object being assigned, put those pointers in the vector, and copy the int values. So that the old pointers are cleaned up, and the object being assigned to becomes a copy of the object being assigned from, without sharing the same pointers.
Your class doesn't respect the rule of 3/5/0. The default-generated move-assignment copy-assignment operator in leak = Leak(); makes leak reference the contents of the temporary Leak object, which it deletes promptly at the end of its lifetime, leaving leak with dangling pointers which it will later try to delete again.
Note: this could have gone unnoticed if your implementation of std::vector systematically emptied the original vector upon moving, but that is not guaranteed.
Note 2: the striked out parts above I wrote without realizing that, as StoryTeller pointed out to me, your class does not generate a move-assignment operator because it has a user-declared destructor. A copy-assignment operator is generated and used instead.
Use smart pointers and containers to model your classes (std::vector<std::array<int, 2>>, std::vector<std::vector<int>> or std::vector<std::unique_ptr<int[]>>). Do not use new, delete and raw owning pointers. In the exceedingly rare case where you may need them, be sure to encapsulate them tightly and to carefully apply the aforementioned rule of 3/5/0 (including exception handling).

What all should be deleted in the destructor of a class

So it's been a while since I've done any c++ coding and I was just wondering which variables in a basic linked list should be deleted in the destructor and unfortunately I can't consult my c++ handbook at the moment regarding the matter. The linked list class looks as follows:
#include <string>
#include <vector>
class Node
{
Node *next;
string sName;
vector<char> cvStuff;
Node(string _s, int _i)
{
next = nullptr;
sName = _s;
for (int i = 0; i < _i; i++)
{
cvStuff.insert(cvStuff.end(), '_');
}
}
~Node()
{
//since sName is assigned during runtime do I delete?
//same for cvStuff?
}
};
I'm also curious, if in the destructor I call
delete next;
will that go to the next node of the linked list and delete that node and thus kind of recursively delete the entire list from that point? Also, if that is the case and I choose for some reason to implement that, would I have to check if next is nullptr before deleting it or would it not make a difference?
Thank you.
Ideally, nothing: use smart pointers: std::unique_ptr<>, std::smart_ptr<>, boost::scoped_ptr<>, etc..
Otherwise, you delete what's an owning native pointer. Is next owning?
Do you plan to delete something in the middle of the list? If yes, you can't delete in the destructor.
Do you plan to share tails? If yes, you need reference-counted smart pointers.
It's okay to delete nullptr (does nothing). In the example, you shouldn't delete sName and cvStuff as those are scoped, thus destroyed automatically.
Also, if this is going to be a list that can grow large, you might want to destroy & deallocate *next manually. This is because you don't want to run out of stack space by recursion.
Furthermore, I suggest separating this to List, meaning the data structure and ListNode, meaning an element. Your questions actually show this ambiguity, that you don't know whether you're deleting the ListNode or the List in the destructor. Separating them solves this.
An object with automatic lifetime has it's destructor called when it goes out of scope:
{ // scope
std::string s;
} // end scope -> s.~string()
A dynamic object (allocated with new) does not have it's destructor called unless delete is called on it.
For a member variable, the scope is the lifetime of the object.
struct S {
std::string str_;
char* p_;
};
int main() { // scope
{ // scope
S s;
} // end scope -> s.~S() -> str_.~string()
}
Note in the above that nothing special happens to p_: it's a pointer which is a simple scalar type, so the code does nothing automatic to it.
So in your list class the only thing you have to worry about is your next member: you need to decide whether it is an "owning" pointer or not. If it is an "owning" pointer then you must call delete on the object in your destructor.
Alternatively, you can leverage 'RAII' (resource aquisition is initialization) and use an object to wrap the pointer and provide a destructor that will invoke delete for you:
{ // scope
std::unique_ptr<Node> ptr = std::make_unique<Node>(args);
} // end scope -> ptr.~unique_ptr() -> delete -> ~Node()
unique_ptr is a purely owning pointer, the other alternative might be shared_ptr which uses ref-counting so that the underlying object is only deleted when you don't have any remaining shared_ptrs to the object.
You would consider your next pointer to be a non-owning pointer if, say, you have kept the actual addresses of the Nodes somewhere else:
std::vector<Node> nodes;
populate(nodes);
list.insert(&nodes[0]);
list.insert(&nodes[1]);
// ...
in the above case the vector owns the nodes and you definitely should not be calling delete in the Node destructor, because the Nodes aren't yours to delete.
list.insert(new Node(0));
list.insert(new Node(1));
here, the list/Nodes are the only things that have pointers to the nodes, so in this use case we need Node::~Node to call delete or we have a leak.
Basically you should just
delete next
and that's all you should do:
The string and vector objects have their own destructors, and since this object is being destructed, theirs will be called.
Deleting a null pointer is not a problem, so you don't even have to check for that.
If next is not a null pointer, it will keep on calling the destructors of the next nodes, on and on, as needed.
Just delete it and that's all, then.
Your class destructor will be called (it is empty), then the member objects destructors are called.
If member is not an object, no destructor is called.
In your example:
- List *next: pointer on List: no destructor called
- string sName: string object: destructor called
- vector<char> cvStuff: vector object: destructor called
Good news: you have nothing to do. Destructor declaration is not even useful here.
If you delete next in your destructor, then deleting an item will delete all other items of your list: not very useful.
(and your List object should rather be called Node). The List is the chained result of all your nodes, held by the first node you created.

Return vector from function without it being destroyed

I have come across an interesting problem. I have a function in C++ that returns a vector filled with classes. Once the vector is returned, it calls deconstructors for each class that is element in the vector.
The problem is an obvious one: the data is destroyed where a class points to the pointers, which get released when the object is destroyed. I can only assume the deconstructors are called because the vector is on the stack, and not on the heap.
So the question is:
Is there anyway to keep returning vector from a function, without it being destroyed? Or would I have to either pass a pointer to return vector as an input to the function?
You can create anything on heap with new. You shouldn't give out from the function the references to your stack objects, as they will be destroyed as soon as the function finishes.
If you prefer your function to return the vector by value, be sure that the objects inside the vector implement copy constructor (and perhaps assignment operator, too, not sure about that). Having that, please do not forget about the Rule of Three.
C++11 should solve your problem using rvalue references. Honestly, I haven't tried it myself, but from what I read it will do exactly what you are trying to do, return the vector without destroying and recreating it (by passing the memory allocated by that vector on to the new vector instead of having the new vector create its own memory and copy the contents over from the old one).
C++ vectors are allocated on the heap. When you return a vector by value a new one will be created with copies of all the elements, then the original elements will be destroyed.
It sounds like you haven't defined your copy constructors properly. For example:
class ThisIsWrong
{
public:
ThisIsWrong()
{
i = new int;
*i = rand();
}
~ThisIsWrong()
{
delete i;
i = nullptr;
}
int value() const
{
return *i;
}
private:
int* i;
};
void foo()
{
vector<ThisIsWrong> wronglets;
wronglets.push_back(ThisIsWrong());
return wronglets;
}
void main()
{
vector<ThisIsWrong> w = foo();
w[0].value(); // SEGFAULT!!
}
You either need to delete the copy and assignment constructors (which will then turn this into a compilation error instead of runtime), or implement them properly.

explicit destructor

The following code is just used to illustrate my question.
template<class T>
class array<T>
{
public:
// constructor
array(cap = 10):capacity(cap)
{element = new T [capacity]; size =0;}
// destructor
~array(){delete [] element;}
void erase(int i);
private:
T *element;
int capacity;
int size;
};
template<class T>
void class array<T>::erase(int i){
// copy
// destruct object
element[i].~T(); ////
// other codes
}
If I have array<string> arr in main.cpp. When I use erase(5), the object of element[5] is destroyed but the space of element[5] will not be deallocated, can I just use element[5] = "abc" to put a new value here? or should I have to use placement new to put new value in the space of element [5]?
When program ends, the arr<string> will call its own destructor which also calls delete [] element. So the destructor of string will run to destroy the object first and then free the space. But since I have explicitly destruct the element[5], does that matter the destructor (which is called by the arr's destuctor) run twice to destruct element[5]? I know the space can not be deallocated twice, how about the object? I made some tests and found it seems fine if I just destruct object twice instead of deallocating space twice.
Update
The answers areļ¼š
(1)I have to use placement new if I explicitly call destructor.
(2) repeatedly destructing object is defined as undefined behavior which may be accepted in most systems but should try to avoid this practice.
You need to use the placement-new syntax:
new (element + 5) string("abc");
It would be incorrect to say element[5] = "abc"; this would invoke operator= on element[5], which is not a valid object, yielding undefined behavior.
When program ends, the arr<string> will call its own destructor which also calls delete [] element.
This is wrong: you are going to end up calling the destructor for objects whose destructors have already been called (e.g., elements[5] in the aforementioned example). This also yields undefined behavior.
Consider using the std::allocator and its interface. It allows you easily to separate allocation from construction. It is used by the C++ Standard Library containers.
Just to explain further exactly how the UD is likely to bite you....
If you erase() an element - explicitly invoking the destructor - that destructor may do things like decrement reference counters + cleanup, delete pointers etc.. When your array<> destructor then does a delete[] element, that will invoke the destructors on each element in turn, and for erased elements those destructors are likely to repeat their reference count maintenance, pointer deletion etc., but this time the initial state isn't as they expect and their actions are likely to crash the program.
For that reason, as Ben says in his comment on James' answer, you absolutely must have replaced an erased element - using placement new - before the array's destructor is invoked, so the destructor will have some legitimate state from which to destruct.
The simplest type of T that illustrates this problem is:
struct T
{
T() : p_(new int) { }
~T() { delete p_; }
int* p_;
};
Here, the p_ value set by new would be deleted during an erase(), and if unchanged when ~array() runs. To fix this, p_ must be changed to something for which delete is valid before ~array() - either by somehow clearing it to 0 or to another pointer returned by new. The most sensible way to do that is by the placement new, which will construct a new object, obtaining a new and valid value for p_, overwriting the old and useless memory content.
That said, you might think you could construct types for which repeated destructor was safe: for example, by setting p_ to 0 after the delete. That would probably work on most systems, but I'm pretty sure there's something in the Standard saying to invoke the destructor twice is UD regardless.

What would the destructor for this class look like?

class Equipment
{
std::vector<Armor*> vEquip;
Weapon* mainWeapon;
int totalDefense;
int totalAttack;
public:
unsigned int GetWeight();
int * GetDefense();
bool EquipArmor(Armor* armor);
bool UnequipArmor(Armor* armor);
bool EquipWeapon(Weapon* wep);
bool UnequipWeapon(Weapon* wep);
Equipment();
virtual ~Equipment();
};
It seems like there should be no destructor. The vector of pointers will take care of itself when it goes out of scope, and the actual objects the pointers point to don't need to be deleted as there will be other references to it.
All of the objects in this refer to the main Container:
class Container
{
int weightLimit;
unsigned int currWeight;
std::vector<Item*> vItems;
public:
bool AddItem(Item* item);
bool RemoveItem(Item* item);
Container();
Container(int weightLim);
Container(int weightLim, std::vector<Item*> items);
~Container();
};
Now here I can see it being necessary to delete all objects in the container, because this is where all the objects are assigned via AddItem(new Item("Blah"))
(Armor and Weapon inherit from Item)
Non-pointer types will take care of themselves. So, ints, floats, objects, etc. will take care of themselves, and you don't have to worry about deleting them.
Any pointers that are managed by the class need to be deleted by that class' destructor. So, if the memory pointed to by the pointer was allocated in the class or if it was given to that class with the idea that that class would manage it, then the destructor needs to delete the pointer.
If the pointer is to memory that another class is managing, then you obviously don't want your destructor to delete it. That's the job of the class that is in charge of the memory pointed to by that pointer.
Standard containers do not manage the pointers that they hold if they hold pointers. So, if you have a container of pointers, whichever class is supposed to manage them needs to delete them. Odds are that that's the class that holds the container, but that depends on what your code is doing.
So, typically, for a class that has a container of pointers, you'll need something like this in the destructor:
for(containerType<T*>::iterator iter = container.begin(),
end = container.end();
iter != end;
++iter)
{
delete *iter;
}
Every pointer that has memory allocated to it has to have something (generally a class, but sometimes the function that it's allocated in) who effectively owns that memory and makes sure that it is freed. When talking about classes, that's usually the same class that the memory is allocated in, but of course, it's quite possible for one class or function to allocate the memory and then effectively pass on the ownership of that memory to another class or function. Regardless, whoever "owns" the memory needs to deal with cleaning it up. That's what your destructor needs to worry about: cleaning up any resources that that class owns.
If you need to delete the items, then:
Container :: ~Container() {
for ( unsigned int i = 0; i < vItems.size(); ++i ) {
delete vItems[i];
}
}
Note that for this to work correctly, the Item class must have a virtual destructor, if the array really contains pointer to Armour and Weapon instances.
If I understand you correctly, you're asking how to delete items from the vItems vector in the main container destructor?
for(std::vector<Item*>::iterator i=vItems.begin(),ie=vItems.end();i!=ie;++i)
delete *i;
Is that what you are asking for?
If your vector would be
std::vector<std::tr1::shared_ptr<Item> >
, then you wouldn't need a destructor. As it is now, the destructor would have to iterate over the vector to delete all items. Easiest with BOOST_FOREACH.
It depends on how you implement it. If the items you add are managed from outside your object (i.e. the body of addItem() looks something like this: { vItems.push_back(items); }), then you won't need a destructor for that one. However, if the items are managed by the object (i.e. addItem() looks like this: { vItems.push_back(new Item(item)); }, then you will need to delete all items in the destructor, because nobody will do that for you.