I'm implementing several data structures in an attempt to learn C++. Below is a binary search tree that I've implemented to learn about pointers, dangling pointers, and memory leaks. I was hoping someone could critique my code, point out any problems that they may find, or any inconsistencies. Please, be as harsh you feel necessary.
Note: As far as I know this implementation works well. Although, I feel as if the remove function could be simplified some how.
BinarySearchTree.h
//
//  BinarySearchTree.h
//  Data Structures
#ifndef __Data_Structures__BinarySearchTree__
#define __Data_Structures__BinarySearchTree__
#include <stdio.h>
#include <functional>
#pragma mark - Enumerations
typedef enum : int
{
    TraversalTypeInOrder,
    TraversalTypePreOrder,
    TraversalTypePostOrder
    
} TraversalType;
#pragma mark -
#pragma mark - Class Definition
template<class T>
class BinarySearchTree
{
    
    
#pragma mark - Structures
    
    template<typename Key>
    struct Node
    {
        Key key;
        
        Node<Key> * left = nullptr;
        Node<Key> * right = nullptr;
        Node<Key> * parent = nullptr;
    };
    
#pragma mark -
    
    
#pragma mark - Private Member Variables
    
    Node<T> * root = nullptr;
    
#pragma mark -
    
    
#pragma mark - Private Helper Functions
    
    T minimum(Node<T> * node)
    {
        if (node->left == nullptr)
            return node->key;
        
        return minimum(node->left);
    }
    
    T maximum(Node<T> * node)
    {
        if (node->right == nullptr)
            return node->key;
        
        return maximum(node->right);
    }
    
#pragma mark -
    
    
#pragma mark - Private Action Functions
    
    void insert(const T &key, Node<T> * node)
    {
        if (key < node->key)
        {
            if (node->left)
            {
                insert(key, node->left);
            }
            else {
                Node<T> * left = new Node<T>();
                left->key = key;
                left->parent = node;
                
                node->left = left;
            }
        }
        else {
            if (node->right)
            {
                insert(key, node->right);
            }
            else {
                Node<T> * right = new Node<T>();
                right->key = key;
                right->parent = node;
                
                node->right = right;
            }
        }
    }
    
    Node<T> * search(const T &key, Node<T> * node)
    {
        if (node == nullptr)
            return nullptr;
        
        if (key == node->key)
        {
            return node;
        }
        else if (key < node->key)
        {
            return search(key, node->left);
        }
        else {
            return search(key, node->right);
        }
    }
    
    void traverse(TraversalType traversalType, std::function<void(T key)> printFunctor, Node<T> * node)
    {
        if (node == nullptr)
            return;
        
        if (traversalType == TraversalTypePreOrder)
            printFunctor(node->key);
        
        traverse(traversalType, printFunctor, node->left);
        
        if (traversalType == TraversalTypeInOrder)
            printFunctor(node->key);
        
        traverse(traversalType, printFunctor, node->right);
        
        if (traversalType == TraversalTypePostOrder)
            printFunctor(node->key);
    }
    
#pragma mark -
    
    
public:
    
#pragma mark - Life Cycle Methods
    
    ~BinarySearchTree()
    {
        removeAll();
    }
    
#pragma mark -
    
    
#pragma mark - Public Helper Functions
    
    T minimum()
    {
        return minimum(root);
    }
    
    T maximum()
    {
        return maximum(root);
    }
    
#pragma mark -
    
    
#pragma mark - Public Actions Functions
    
    void insert(const T &key)
    {
        if (root == nullptr)
        {
            root = new Node<T>();
            root->key = key;
        }
        else {
            insert(key, root);
        }
    }
    
    void remove(const T &key)
    {
        Node<T> * node = search(key);
        
        if (node == nullptr)
            return;
        
        if (node->left != nullptr && node->right != nullptr)
        {
            T successorKey = minimum(node->right);
            remove(successorKey);
            
            node->key = successorKey;
        }
        else if (node->left == nullptr && node->right == nullptr)
        {
            if (node == root)
            {
                root = nullptr;
            }
            else {
                if (node == node->parent->left)
                    node->parent->left = nullptr;
                else
                    node->parent->right = nullptr;
            }
            
            delete node;
        }
        else {
            if (node == root)
            {
                if (node->left != nullptr)
                    root = node->left;
                else
                    root = node->right;
            }
            else {
                T successorKey;
                
                if (node->left != nullptr)
                {
                    successorKey = maximum(node->left);
                    remove(successorKey);
                    
                    node->key = successorKey;
                }
                else {
                    successorKey = minimum(node->right);
                    remove(successorKey);
                    
                    node->key = successorKey;
                }
            }
        }
    }
    
    Node<T> * search(const T &key)
    {
        return search(key, root);
    }
    
    void traverse(TraversalType traversalType, std::function<void(T key)> printFunctor)
    {
        traverse(traversalType, printFunctor, root);
    }
    
    void removeAll()
    {
        if (root == nullptr)
            return;
        
        remove(root->key);
        removeAll();
    }
    
#pragma mark -
    
};
#pragma mark -
#endif /* defined(__Data_Structures__BinarySearchTree__) */
main.cpp
//
//  main.cpp
//  Data Structures
#include <iostream>
#include <cstdlib>
#include "BinarySearchTree.h"
int main(int argc, const char * argv[])
{
    srand((unsigned)time(NULL));
    
    BinarySearchTree<int> binarySearchTree;
    
    binarySearchTree.insert(11);
    binarySearchTree.insert(9);
    binarySearchTree.insert(8);
    binarySearchTree.insert(10);
    binarySearchTree.insert(14);
    binarySearchTree.insert(13);
    binarySearchTree.insert(15);
    
    auto printNode = [](int key) -> void { printf("%d ", key); };
    
    binarySearchTree.traverse(TraversalTypePreOrder, printNode);
    printf("\n");
    binarySearchTree.remove(10);
    printf("\n");
    binarySearchTree.traverse(TraversalTypePreOrder, printNode);
    
    return 0;
}
 
                