Binary Search Tree implementation using templates I wrote this implementation using templates. It works fine and as expected. Just want to know if this can be improved. I have some specific questions too at the end. Please feel free to critique to your heart's content.

#include &lt;iostream&gt;

template &lt;class T&gt;
class Tree
{
    // Internal class which stores only Node related information.
    struct TreeNode
    {
        T data;
        TreeNode * left;
        TreeNode * right;

        TreeNode(T val):data(val),left(NULL),right(NULL)
        {
        }
    };
    TreeNode * root;
    void print(TreeNode*);
    void freeMemory(TreeNode*);


public:
    Tree();
    ~Tree();
    void insert(T);
    void print();
};

template &lt;class T&gt;
Tree&lt;T&gt;::Tree():root(NULL){}

template &lt;class T&gt;
Tree&lt;T&gt;::~Tree()
{
    freeMemory(root);
}

template &lt;class T&gt;
void Tree&lt;T&gt;::freeMemory(Tree::TreeNode *node)
{
    if (node==NULL)
        return;
    if (node-&gt;left)
        freeMemory(node-&gt;left);
    if (root-&gt;right)
        freeMemory(node-&gt;right);
    delete node;
}

template &lt;class T&gt;
//make it return value?
void Tree&lt;T&gt;::insert(T val)
{
    TreeNode * treeNode = NULL;
    try
    {
        treeNode = new TreeNode(val); // handle exception necessary?
    } catch (std::bad_alloc &amp;exception)
    {
        std::cerr &lt;&lt; "bad_alloc caught: " &lt;&lt; exception.what() &lt;&lt; std::endl;
        EXIT_FAILURE;
    }
    TreeNode *temp=NULL;
    TreeNode *prev=NULL;

    temp = root;
    while(temp)
    {
        prev = temp;
        if (temp-&gt;data &lt; treeNode-&gt;data)
            temp = temp-&gt;right;
        else
            temp = temp-&gt;left;
    }
    if (prev==NULL)
        root = treeNode;
    else
    {
        if (prev-&gt;data&lt;treeNode-&gt;data)
            prev-&gt;right = treeNode;  // use setter function?
        else
            prev-&gt;left = treeNode;
    }
}

template &lt;class T&gt;
void Tree&lt;T&gt;::print(TreeNode *root)
{
    if (root==NULL)
        return ;
    print(root-&gt;left);
    std::cout &lt;&lt; root-&gt;data &lt;&lt; std::endl;
    print(root-&gt;right);
}

template &lt;class T&gt;
void Tree&lt;T&gt;::print()
{
    print(root);
}


int main()
{
    Tree&lt;int&gt; tree;
    tree.insert(14);
    tree.insert(12);
    tree.insert(6);
    tree.insert(17);
    tree.insert(8);
    tree.print();

}



Should I handle possible exceptions that can be thrown from new?
Is it good practice to have a TreeNode class as I've used?  If not, how to make it better?
Should my insert return some value to indicate if insertion was successful?
Any other comments/suggestions?
Exception handling

While it might make sense to handle the bad_alloc exception, the way you've done so is likely to lead to problems. The basic point of exception handling is that it decouples the point at which the exception is handled from the point at which it is generated. Right now, you've couple those by catching the bad_alloc immediately after the point at which it could be generated. Worse, you haven't handled it well at all. It looks like you intended to exit (stop execution), but in fact you only have EXIT_FAILURE, which is just a constant (typically 1). At least in theory, that's evaluated by the compiler as an expression which does nothing. In fact, there's a good chance the compiler will optimize such an expression way completely, so it does nothing.

Even assuming you corrected that so it exited the program correctly, code at that level (allocating a node) has no business deciding that bad_alloc should lead directly to exiting the program. In your test code that may make sense, but depending on how the search tree is being used, it make may sense to react to a bad_alloc in some other way. Leave that decision to the higher level code that can handle it more appropriately.

For example, it would make much more sense to handle it in main:

int main()
{    
    try {
        Tree&lt;int&gt; tree;
        tree.insert(14);
        tree.insert(12);
        tree.insert(6);
        tree.insert(17);
        tree.insert(8);
        tree.print();
    }
    catch (std::exception const &amp;e) { 
        std::cerr &lt;&lt; e.what &lt;&lt; "\n";
    }
}


With that in place, your code to allocate a node:

    TreeNode * treeNode = NULL;
    try
    {
        treeNode = new TreeNode(val); // handle exception necessary?
    } catch (std::bad_alloc &amp;exception)
    {
        std::cerr &lt;&lt; "bad_alloc caught: " &lt;&lt; exception.what() &lt;&lt; std::endl;
        EXIT_FAILURE;
    }


...becomes more like:

TreeNode *treeNode = new TreeNode(val);


...Only in really shouldn't be quite that either, because (as detailed below) you should really use unique_ptr instead of raw pointers.

bad_alloc specifics

bad_alloc, however, is kind of a special case, and I sometimes question whether it makes sense to handle it at all, at least in code intended for most typical desktop OSes. Memory allocation failure used to be pretty simple: you had N kilobytes of memory, and when/if that ran out, malloc returned NULL or new threw an exception.

Virtual memory changed that though. When you run out of memory, the OS swaps data to the swap file to make more room. In most typical cases, long before allocation has truly failed, the system will run slow enough for long enough that the user will kill the program long before the memory allocation actually fails. In fact, Linux actually kind of automates that process--it has an "OOM Killer" process that will kill processes to keep memory free so the system doesn't drag down to running too slowly. In most cases, that means your process will be killed without bad_alloc ever being thrown.

As such, it's quite debatable (at best) whether it makes sense to try to deal specifically with bad_alloc, at least in code for these systems.

For an interview, handling bad_alloc would depend. For embedded software, you typically do want to handle it. For desktop/server software, you may not want to, but if you don't you need to be prepared to explain why you don't.

"Raw" pointers

Most of what you've currently defined as pointers could really be instances of std::unique_ptr instead (again, at least assuming you're using a new enough compiler to support it). unique_ptr automates most of the "cleanup", so you could pretty much (probably completely) eliminate what you've currently implemented as freeMemory.

The downside of this is that a unique_ptr stores a deleter object that's used to clean up the pointee object when the unique_ptr goes out of scope. In the case of your binary tree, you'd at least normally assume that all the nodes in the tree to be deleted the same way. In that case, you probably either store no deleter object at all (and just use delete) or else store only one deleter for the entire tree, and internally use raw pointers. This can save quite a bit of storage space with (at worst) fairly minimal cost in code complexity.

Recursion pattern

Although I've already pointed out how it can probably be eliminated completely, let's assume for the moment that you're using raw pointers so you still need your freeMemory. If so, it can (IMO) be cleaned up a bit. Instead of checking for null pointers before you make the recursive call, just check for a null pointer once:

template &lt;class T&gt;
void Tree&lt;T&gt;::freeMemory(Tree::TreeNode *node)
{
    if (node==nullptr)
        return;
    freeMemory(node-&gt;left);
    freeMemory(node-&gt;right);
    delete node;
}


Each chain of recursive calls will stop when you reach a null pointer (thanks to the one if that remains). This reduces the complexity of this function pretty noticeably at essentially no cost1.

Print

As @Jamal already pointed out, your print function doesn't modify the tree, so it should be const. I'd also prefer to see it allow the user to pass a specific stream to which the contents will be printed. In fact, it's at least worth considering overloading the stream insertion operator (operator&lt;&lt;) for your tree type, so you can insert a tree in a stream just like you would any other type:

Tree my_tree;

// code to insert data into tree goes here

std::cout &lt;&lt; my_tree;


Alternative to Print

I'd at least consider whether you really want a Tree::print at all, or whether it's better to have the ability to walk the tree and apply essentially any function to all its nodes (with print as just one of those possibilities). 

Return value from insert

I would say that yes, it's a good idea for insert to return a value indicating whether the insertion was successful. Your code is roughly analogous to std::set, which does return such a value, and I've used that return value quite a few times, so if I were going to use this, I'd probably make use of the same.

Iterators

If you were going to put this to real use, it would be preferable to be able to use it via a standard iterator interface. Alternatively, you might prefer to support a range interface (I'd probably prefer to use a range-based interface, anyway). This is probably getting beyond the scope of what you're currently thinking about though. You also quickly run into the fact that this provides pretty much a subset of what std::set already provides, so its practical use is probably limited in any case.




1. And in case you're wondering about the apparent contradiction of using nullptr in code that's only needed prior to C++11 (when nullptr was introduced), it's really pretty simple: nullptr is sufficiently trivial that a fair number of compilers supported it before they implemented rvalue references, which are crucial for unique_ptr. As such, even if you're using an older compiler that doesn't have unique_ptr there's a good chance it does have nullptr. It's even possible to define a nullptr class that works with C++03 compilers (a little Googling should turn up at least one).