0

Suppose I have a binary tree class whose purpose is to inductively cut a real interval (a, b) into many small intervals, picking midpoints. NB: the class I'm actually writing deals with triangles in the plane but the idea is the same.

Here is what the class looks like in the header file:

class Tree
{
public:
    Tree(double &a, double &b, int depth);
    ~Tree();

    Tree getCopy() const;

private:        
    Tree(double *a, double *b, int depth, int maxDepth);

    double *a, *b;
    int depth, maxDepth;    
    Tree *leftChild, *rightChild;
};

Note that the tree stores pointers towards doubles a and b, rather than the actual doubles. The reason for that is to save memory (and speed?), observing that a and b are going to be shared by many children trees (I know that a double is quite "light" but in my actual class, I have something "heavier").

Now here is the main constructor:

Tree::Tree(double *a, double *b, int depth, int maxDepth) :
    depth(depth), maxDepth(maxDepth)
{    
    if (depth == maxDepth)
    {
        this->a = new double(*a);
        this->b = new double(*b);
    }
    else
    {
        this->a = a;
        this->b = b;
    }


    if (depth == 0)
    {
        leftChild = 0;
        rightChild = 0;
    }
    else
    {
        double * midpoint = new double((*a+*b)/2);
        leftChild = new Tree(a, midpoint, depth - 1, maxDepth);
        rightChild = new Tree(midpoint, b, depth - 1, maxDepth);
    }

}

And the destructor:

Tree::~Tree()
{
    if (depth == 0)
    {
        delete b;
    }
    else
    {
        delete leftChild;
        delete rightChild;
    }

    if (depth == maxDepth)
    {
        delete a;
    }
}

I hope both of these functions are correct. Notice that the constructor is private, it is the one that is called recursively. The public constructor is the following:

Tree::Tree(double &a, double &b, int depth)
{
    *this = *(new Tree(&a, &b, depth, depth));
}

I know this looks weird, and I'm worried I might be creating a memory leak by doing this? But on the other hand, if I wrote:

    *this = Tree(&a, &b, depth, depth);

Wouldn't that fail? Let me try to explain why I think it might fail by considering the equivalently function

{
    Tree T(&a, &b, depth, depth);
    *this = T;
}

I'm thinking that as soon as this function is exited, the object T is destroyed, therefore the children are deleted etc.

The same concern goes for the copy function:

Tree Tree::getCopy() const
{
    return Tree(a, b, depth, depth);
}

So the question is: what is the correct way to write these functions? I'm also open to hearing general remarks about the way I write this class. Thanks in advance!

Seub
  • 1,006
  • 1
  • 15
  • 28
  • `*this = *(new Tree(&a, &b, depth, depth));` Don't do this!!! – Neil Kirk Aug 08 '13 at 19:42
  • Actually, I think this kind of question belong more to [Code Review](http://codereview.stackexchange.com); but I agree the frontier is not very sharp for this kind of question... – Kyle_the_hacker Aug 08 '13 at 19:48

3 Answers3

1

You are creating a leak here:

Tree::Tree(double &a, double &b, int depth)
{
    *this = *(new Tree(&a, &b, depth, depth));
}

You allocate new Tree object using new and you don't delete it anywhere.

Additionally you are not following the rule of three. You created a class which implements destructor, and the rule of three says that (usually) when you create any of: copy constructor, assignment operator or destructor then you usually need all of them.

I suggest you make Tree(double *a, double *b, int depth, int maxDepth) a static function and you call that from your public constructor(s) as needed.

Mooing Duck
  • 56,371
  • 16
  • 89
  • 146
Tomek
  • 4,093
  • 1
  • 17
  • 17
  • Thank you for your answer. I think this is a good suggestion, in any case the simplest for me to use. As for the rule of three, I'll look into it. – Seub Aug 08 '13 at 19:58
  • I'll mark utnapistim's as the accepted answer, but I could have picked this one as well. Thanks again. – Seub Aug 08 '13 at 23:05
1

The public constructor is the following:

Tree::Tree(double &a, double &b, int depth)
{
    *this = *(new Tree(&a, &b, depth, depth));
}

I know this looks weird, and I'm worried I might be creating a memory leak by doing this?

You are (indeed) creating a memory leak.

One solution would be to re-write it like this:

new(this) Tree(&a, &b, depth, depth);

Placement-new would not allocate the memory but still make the call. I'm not sure if there are any hidden pitfalls with this though.

The solution is still weird though. If you are working with C++11, you should implement in terms of a move-constructor (and if possible, forward the constructor call). Otherwise, you should implement in terms of std::shared_ptr (and/or maybe the pimpl idiom).

Edit: Another (more) elegant solution would be to implement "the big three and a half" (void Tree::swap(Tree& x); and the big three); Then, you could write:

{
     swap(Tree(&a, &b, depth, depth));
}
utnapistim
  • 24,817
  • 3
  • 41
  • 76
  • Calling placement new on object being constructed will overwrite initialization made in initializer list and in custructor body prior to placement new invocation. If any of the subobjects constructed before we entered constructor body took any resources, these resources are gone. – Tomek Aug 08 '13 at 19:47
  • Thank you for your answer utnapistim. Unfortunately I am not acquainted with the notions of placement new, move-constructor, std::shared_ptr, the pimpl idiom, the big three and half, so pretty much everything you wrote! But this is great, I want to learn about all this. – Seub Aug 08 '13 at 20:04
  • 1
    have a look here for the [copy&swap idiom](http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom). Also, [the big three](http://en.wikipedia.org/wiki/Rule_of_three_%28C%2B%2B_programming%29). (and [placement new](http://stackoverflow.com/questions/222557/what-uses-are-there-for-placement-new)). – utnapistim Aug 08 '13 at 20:10
  • Ok, I've read about all this. I'm probably going to implement the "big three and a half" (or possibly the "big five" or whatever for C++11). I could also be content with Tomek's suggestion for now. Thanks again. – Seub Aug 08 '13 at 23:02
  • One more question, if I may: – Seub Aug 09 '13 at 02:21
  • One more question, if I may (sorry for previous comment): Following your link, I implemented the "big three and a half" with a swap function that looks like `friend void swap(Tree &first, Tree &second);` (rather than `void Tree::swap(Tree& x);`). In any case, I don't think what you wrote works because `Tree(&a, &b, depth, depth)` is an rvalue (and thus can't be passed as a reference). So instead I use my brand new copy-assignment operator and write `*this = Tree(&a, &b, depth, depth)`. Is that okay? – Seub Aug 09 '13 at 02:27
  • Rats, it does not work. I think the problem is that when I call the copy-assignment operator (`*this = Tree(&a, &b, depth, depth)`), the copy constructor should be called (since the argument of the copy-assignement operator is passed by value), but it seems that this is not happening. I don't understand why. Help? – Seub Aug 09 '13 at 03:44
0

I think maxDepth is the same for every objects of the class, so make it static (and you will only need one initialization of its value):

static int maxDepth;

And then I would rewrite the private and public constructor in one constructor (your actual public one make a memory leak):

Tree::Tree(double &a, double &b, int depth)
{
    // stuff from private constructor
}

Try to stay consistent with the types you use:

double * midpoint = new double((*a + *b) / 2.0);   // 2.0 is double; 2 is int

And finally I really don't get the:

Tree(&a, &b, depth, depth);  // why ... depth, depth ... ???

You said, you don't wanted to waste memory and so you used pointer, but actually the pointer also have a size... If only the leafs of your tree contain a and b (but it seems to me, this isn't the case here), you could do something like this:

class Tree
{
    // functors declarations

    bool isLeaf = false;
    int depth, maxDepth;    
    Tree *leftChild, *rightChild;
};

Tree::Tree(double a, double b, int depth): depth(depth) {
    if (depth == maxDepth) {
            isLeaf = true;
            this->leftChild = new double(a);
            this->rightChild = new double(b);
    }
}

And then if isLeaf is false look for the children and if it's true look for the values.

Kyle_the_hacker
  • 1,326
  • 1
  • 10
  • 27