1

When considering a templated data strucuture like a BinaryTree, what is the cleanest way to deal with pointer types?

If the tree holds pointers, the memory pointed to by them must be returned to the heap on deletion of sub-trees. But how can I tell from inside the BinaryTree class, wether it was allocated by malloc or new?

One solution would be to set flags, indicating the type of data stored. But than my class would rely on correct usage from outside...

Another idea was passing some kind of vector to all the BinaryTree methods that might delete a sub-tree. Whenever a sub-tree is deleted, it adds the stored data to the vector and in the end I can handle them correctly from outside the BinaryTree.

But that sounds like a cumbersome solution to me and there might be a better way.

x squared
  • 3,173
  • 1
  • 26
  • 41

5 Answers5

4

The only reason for storing raw pointers in a container like BinaryTree is if you want to reference objects stored/managed elsewhere. In that case, simply deleting the pointers (not the objects pointed to by them) is enough, because the deletion of the objects is handled elsewhere.

If you want to manage dynamically allocated objects in your tree, use e.g. std::unique_pointer<T> as the element type of the container. In that case, again simply deleting the stored element will do, because deleting the std::unique_pointer<T> implies deleting the object pointed to by it.

gTcV
  • 2,446
  • 1
  • 15
  • 32
  • Thank you, I didn't know about unique pointers. Also the first part of the answer makes a lot of sense. – x squared Feb 04 '14 at 10:17
2

First of all: in C++ avoid malloc. Then you don't have to worry about whether something was allocated with malloc or new, because you never allocated anything with malloc. Although your problem isn't solved even then, because there is a difference between delete and delete[] for arrays... so you can't really go deleting other people's pointers for them. I suppose a good mantra would be "in destructors we trust". :)

Secondly: if you are trying to create a container class, don't hand it raw pointers if you want it to participate in resource management. Think about what happens when you make a std::vector<std::string *>. When that vector runs its destructor, does it do anything with the pointers contained? Nope. As far as it is concerned it might as well have been holding integers. The lifetime management is assumed to be taken care of elsewhere. (At least, we hope...!)

If you do want your container to participate in lifetime management, you need to pass it objects that wrap up your pointers and speak some protocol. While you're free to define any protocol you like, the easiest protocols to work with are the ones that have already been standardized. In C++ that's things like whether your object supports copy construction, move construction, the ordinary destructor... etc. Looking to the standard library containers for inspiration is a good start.

So consider your idea of your container putting objects in a destroy queueing vector, and then calling a method to destroy them. There may be an advantage to destroying objects in batches, but why would a general purpose container care about that? Why can't the contained objects run their destructors as normal, but have that destructor put the wrapping object's content into a queue maintained by some kind of "contained object manager"? You can separate the concern from the container completely, if that's what you really need. Usually though, you just don't need it.

As mentioned by others, look into shared_ptr and unique_ptr if you haven't already, and you might learn something from the case of why auto_ptr was decided to be not cool

1

Use shared pointers (boost::shared_ptr or std::shared_ptr from c++11) for storing your data in tree.

Michał Walenciak
  • 4,257
  • 4
  • 33
  • 61
1

Lots of good advice already, to which I'll just add one powerful (but probably overkill) alternative technique: if you look at the Standard containers you'll notice they're not only templated on the element type - they also accept an Allocator policy. You can use the same technique to provide flexible resource allocation, not only for elements but also for the memory the container may need for nodes or other data. Sadly, C++03 expected allocators not to have state so they weren't very useful, but there's no reason you can't allow this if you're designing your own container. If you're interested in this type of technique, you might want to read Modern C++ Design by Andrei Alexandrescu.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
0

There are several question here. I'm answering to "But how can I tell from inside the BinaryTree class, wether it was allocated by malloc or new?"

The answer is simple. Don't mix C++ alloc (new / delete) with C alloc (malloc / free) !. It won't work. See What is the "correct" way to reconcile malloc and new in a mixed C/C++ program? or http://www.codeproject.com/Articles/6555/To-new-is-C-To-malloc-is-C-To-mix-them-is-sin for more details.

Community
  • 1
  • 1
hivert
  • 10,579
  • 3
  • 31
  • 56
  • I don't think that's what he meant... I guess he meant to ask how to tell wether an object was dynamically allocated at all. – JorenHeit Feb 04 '14 at 10:14