views:

98

answers:

4

I'm still working on my binary trees, and the insertion, lookup, maximum, minimum functions are all working great so far. So I want to do a deletion function next. I included a Stack which saves- ah hell, I'll just show the code:

class Tree
{
private:
    int value_;
    Tree *root_;
    Tree *left_;
    Tree *right_;

    std::stack<Tree*> treeStack;

The delete function:

int Tree::eraseTree()
{
    while( !treeStack.empty() )
    {
        Tree *temp = treeStack.top();
        treeStack.pop();
        delete temp;
    }
    if( treeStack.empty() )
        return 1;
    else
        return -1;
}

I'm getting errors now. This wouldn't be much of a problem- I try to debug my own code- except this time it's telling me there is an error in the <deque> library file which I am not even using.

Before the program shuts off I get System.AccessViolationException and the faulty code points to the deque file. Of course it can't be there, it has to be some pointer in my code. Which leads me to believe I am not working the stack correctly, or not pushing to the stack correctly.

What am I doing wrong here? Am I actually deleting the nodes when I call .pop on the stack?

EDIT:

if( !root_ )
{
    root_ = new Tree(val, 0, 0);
    treeStack.push(root_);
    return val;
}

And

Tree *parent = findInsertionPoint(val, root_);
    if( val < parent->value_ )
        parent->left_  = new Tree(val, 0, 0);
    else
        parent->right_ = new Tree(val, 0,0);

    treeStack.push(parent);
    return val;

Is where I am pushing my elements on the stack.

Additional Question: Does a std::stack have to be built in the ctor?

+1  A: 

It sounds like you might be deleting something twice.

Make sure the stuff you're deleting isn't used or deleted anywhere else, especially after calling your eraseTree() method.

Raceimaztion
I've included the insertion of elements, and no, this is my first delete function. The erase function is the last thing called in my program so far.
SoulBeaver
Have you tried leaving the delete out?I've found, annoyingly enough, that many times C++ automatically deletes everything I've allocated, and complains if I delete it myself.
Raceimaztion
+2  A: 

Change root_*, left_* and right_* into auto_ptrs to guarantee their destruction. Then when you come to delete a node, you can just delete root_.release(); and all is fine. Gotta ask why you're using a stack.

DeadMG
@DeadMG: I'm not understanding what you mean. If I want to delete the entire tree without having to recur down the tree and then back up, I simply store all the pointers in the stack. The first pointers extracted are on the bottom of the tree. Then I just delete the content. That's why I want a stack.How would auto_ptrs help me do that?
SoulBeaver
@SoulBeaver: You don't have to recur up and down the tree if you use auto_ptrs. You could just delete the root node and the whole tree goes down. With auto_ptr, destruction is automated and guaranteed. You don't need to bother with any of the stack crap.
DeadMG
@DeadMG: I don't think that auto_ptr is a good choice... I'd suggest using shared_ptr instead.
EmbeddedProg
@EmbeddedProg: Why? Only the tree node has the right to delete it's children- nobody else. Although I just realized that perhaps root_ shouldn't be an auto_ptr- didn't twig that it pointed back to the first node.
DeadMG
+2  A: 

You're crashing in deque because the stack contains one of those. If you look at the stack trace after the crash, then you can find out what your code was doing when it happened.

It looks like the final snippet of code is putting the wrong node on the stack; surely it should be the newly created node, not the insertion point? If you add two nodes to a parent, then the parent will end up in the stack twice, and will be deleted twice.

It should probably be more like

Tree *parent = findInsertionPoint(val, root_);
Tree *child = new Tree(val, 0, 0);
if( val < parent->value_ )
    parent->left_  = child;
else
    parent->right_ = child;

treeStack.push(child);
return val;

But I'd favour DeadMGs suggestion to use smart pointers for left_ and right_ (but don't make root_ an auto_ptr, if that's shared by all the nodes), and let them clean up for you. I'm not sure I see the point of the stack; if it's to speed up the tree's destruction, then ask yourself two questions before adding a complex and error-prone optimisation:

  • is it slow enough to be worth the development/debugging cost of optimising?
  • is it worth the extra runtime and memory cost of building and maintaining the stack alongside the tree?

And your additional question: the stack will be built in the constructor, but you don't have to write any code to do it. Its default constructor will be called automatically, and that will give you a usable, empty stack.

Also, unless this is a learning exercise, you should use std::multiset rather than reinventing it.

Mike Seymour
+1 for pointing out that std::stack is an adaptor which uses std::deque by default. I wouldn't call this a useless exercise though. Implementing a tree is good academic practice! Maybe he'll want to implement a kd-tree, trie, or some other tree structure some day for which there is no handy library accessible for the platform(s) he's working on. It'll also help him to appreciate how superior std::set/std::map actually are.
@stinky472: I didn't call it a "useless exercise". As you say, implementing a tree is certainly an important thing to learn about.
Mike Seymour
+1  A: 

You got an error in deque because by default std::stack uses this class as an internal container. Look at the stack definition:

template<class _Ty, class _Container = deque<_Ty> > class stack;
Peter