Binary Search Tree implementation using smart pointers












8












$begingroup$


I have implemented below code for binary search tree implementation using shared pointer. At present, I have considered only integers. It supports insertion and deletion of values. Also, a print method is implemented for inorder traversal.



Please review and provide your suggestions.



class Bst {
struct node {
int val;
shared_ptr<node> left, right;
node(int v) : val(v), left(nullptr), right(nullptr){}
};
shared_ptr<node> root;
shared_ptr<node> _insert(shared_ptr<node> curr, int val);
shared_ptr<node> _del(shared_ptr<node> curr, int val);
shared_ptr<node> findmin(shared_ptr<node> curr);
void _print(shared_ptr<node> curr);

public:
void insert(int val) { root = _insert(root, val); }
void del(int val) { root = _del(root, val); }
void print() { _print(root); cout << 'n'; }
};

shared_ptr<Bst::node> Bst::_insert(shared_ptr<node> curr, int val)
{
if (curr == nullptr)
return make_shared<node>(val);

if (val < curr->val)
curr->left = _insert(curr->left, val);
else
curr->right = _insert(curr->right, val);

return curr;
}
shared_ptr<Bst::node> Bst::findmin(shared_ptr<node> curr)
{
while (curr->left)
curr = curr->left;
return curr;
}
shared_ptr<Bst::node> Bst::_del(shared_ptr<node> curr, int val)
{
if (val < curr->val)
curr->left = _del(curr->left, val);
else if (val > curr->val)
curr->right = _del(curr->right, val);
else {
if (curr->right == nullptr)
return curr->left;
if (curr->left == nullptr)
return curr->right;
shared_ptr<node> temp = findmin(curr->right);
curr->val = temp->val;
curr->right = _del(curr->right, curr->val);
}
return curr;
}
void Bst::_print(shared_ptr<node> curr)
{
if (curr == nullptr)
return;
_print(curr->left);
cout << curr->val << " ";
_print(curr->right);
}









share|improve this question







New contributor




bornfree is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.







$endgroup$

















    8












    $begingroup$


    I have implemented below code for binary search tree implementation using shared pointer. At present, I have considered only integers. It supports insertion and deletion of values. Also, a print method is implemented for inorder traversal.



    Please review and provide your suggestions.



    class Bst {
    struct node {
    int val;
    shared_ptr<node> left, right;
    node(int v) : val(v), left(nullptr), right(nullptr){}
    };
    shared_ptr<node> root;
    shared_ptr<node> _insert(shared_ptr<node> curr, int val);
    shared_ptr<node> _del(shared_ptr<node> curr, int val);
    shared_ptr<node> findmin(shared_ptr<node> curr);
    void _print(shared_ptr<node> curr);

    public:
    void insert(int val) { root = _insert(root, val); }
    void del(int val) { root = _del(root, val); }
    void print() { _print(root); cout << 'n'; }
    };

    shared_ptr<Bst::node> Bst::_insert(shared_ptr<node> curr, int val)
    {
    if (curr == nullptr)
    return make_shared<node>(val);

    if (val < curr->val)
    curr->left = _insert(curr->left, val);
    else
    curr->right = _insert(curr->right, val);

    return curr;
    }
    shared_ptr<Bst::node> Bst::findmin(shared_ptr<node> curr)
    {
    while (curr->left)
    curr = curr->left;
    return curr;
    }
    shared_ptr<Bst::node> Bst::_del(shared_ptr<node> curr, int val)
    {
    if (val < curr->val)
    curr->left = _del(curr->left, val);
    else if (val > curr->val)
    curr->right = _del(curr->right, val);
    else {
    if (curr->right == nullptr)
    return curr->left;
    if (curr->left == nullptr)
    return curr->right;
    shared_ptr<node> temp = findmin(curr->right);
    curr->val = temp->val;
    curr->right = _del(curr->right, curr->val);
    }
    return curr;
    }
    void Bst::_print(shared_ptr<node> curr)
    {
    if (curr == nullptr)
    return;
    _print(curr->left);
    cout << curr->val << " ";
    _print(curr->right);
    }









    share|improve this question







    New contributor




    bornfree is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.







    $endgroup$















      8












      8








      8


      2



      $begingroup$


      I have implemented below code for binary search tree implementation using shared pointer. At present, I have considered only integers. It supports insertion and deletion of values. Also, a print method is implemented for inorder traversal.



      Please review and provide your suggestions.



      class Bst {
      struct node {
      int val;
      shared_ptr<node> left, right;
      node(int v) : val(v), left(nullptr), right(nullptr){}
      };
      shared_ptr<node> root;
      shared_ptr<node> _insert(shared_ptr<node> curr, int val);
      shared_ptr<node> _del(shared_ptr<node> curr, int val);
      shared_ptr<node> findmin(shared_ptr<node> curr);
      void _print(shared_ptr<node> curr);

      public:
      void insert(int val) { root = _insert(root, val); }
      void del(int val) { root = _del(root, val); }
      void print() { _print(root); cout << 'n'; }
      };

      shared_ptr<Bst::node> Bst::_insert(shared_ptr<node> curr, int val)
      {
      if (curr == nullptr)
      return make_shared<node>(val);

      if (val < curr->val)
      curr->left = _insert(curr->left, val);
      else
      curr->right = _insert(curr->right, val);

      return curr;
      }
      shared_ptr<Bst::node> Bst::findmin(shared_ptr<node> curr)
      {
      while (curr->left)
      curr = curr->left;
      return curr;
      }
      shared_ptr<Bst::node> Bst::_del(shared_ptr<node> curr, int val)
      {
      if (val < curr->val)
      curr->left = _del(curr->left, val);
      else if (val > curr->val)
      curr->right = _del(curr->right, val);
      else {
      if (curr->right == nullptr)
      return curr->left;
      if (curr->left == nullptr)
      return curr->right;
      shared_ptr<node> temp = findmin(curr->right);
      curr->val = temp->val;
      curr->right = _del(curr->right, curr->val);
      }
      return curr;
      }
      void Bst::_print(shared_ptr<node> curr)
      {
      if (curr == nullptr)
      return;
      _print(curr->left);
      cout << curr->val << " ";
      _print(curr->right);
      }









      share|improve this question







      New contributor




      bornfree is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.







      $endgroup$




      I have implemented below code for binary search tree implementation using shared pointer. At present, I have considered only integers. It supports insertion and deletion of values. Also, a print method is implemented for inorder traversal.



      Please review and provide your suggestions.



      class Bst {
      struct node {
      int val;
      shared_ptr<node> left, right;
      node(int v) : val(v), left(nullptr), right(nullptr){}
      };
      shared_ptr<node> root;
      shared_ptr<node> _insert(shared_ptr<node> curr, int val);
      shared_ptr<node> _del(shared_ptr<node> curr, int val);
      shared_ptr<node> findmin(shared_ptr<node> curr);
      void _print(shared_ptr<node> curr);

      public:
      void insert(int val) { root = _insert(root, val); }
      void del(int val) { root = _del(root, val); }
      void print() { _print(root); cout << 'n'; }
      };

      shared_ptr<Bst::node> Bst::_insert(shared_ptr<node> curr, int val)
      {
      if (curr == nullptr)
      return make_shared<node>(val);

      if (val < curr->val)
      curr->left = _insert(curr->left, val);
      else
      curr->right = _insert(curr->right, val);

      return curr;
      }
      shared_ptr<Bst::node> Bst::findmin(shared_ptr<node> curr)
      {
      while (curr->left)
      curr = curr->left;
      return curr;
      }
      shared_ptr<Bst::node> Bst::_del(shared_ptr<node> curr, int val)
      {
      if (val < curr->val)
      curr->left = _del(curr->left, val);
      else if (val > curr->val)
      curr->right = _del(curr->right, val);
      else {
      if (curr->right == nullptr)
      return curr->left;
      if (curr->left == nullptr)
      return curr->right;
      shared_ptr<node> temp = findmin(curr->right);
      curr->val = temp->val;
      curr->right = _del(curr->right, curr->val);
      }
      return curr;
      }
      void Bst::_print(shared_ptr<node> curr)
      {
      if (curr == nullptr)
      return;
      _print(curr->left);
      cout << curr->val << " ";
      _print(curr->right);
      }






      c++ object-oriented c++11 design-patterns pointers






      share|improve this question







      New contributor




      bornfree is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.











      share|improve this question







      New contributor




      bornfree is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      share|improve this question




      share|improve this question






      New contributor




      bornfree is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      asked yesterday









      bornfreebornfree

      1433




      1433




      New contributor




      bornfree is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.





      New contributor





      bornfree is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






      bornfree is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






















          3 Answers
          3






          active

          oldest

          votes


















          9












          $begingroup$


          • please do not using namespace std;

          • why has Bst::findmin no prefix _?

          • why do you use std::shared_ptr<> at all? std::shared_ptr is ment to represent shared ownership. This makes no sense for tree nodes, you're not going to have two trees with the same nodes. std::unique_ptr<> is a better candidate.

          • it is generally good to separate the data structure from its serialization. Make print a separate function and also handle the member printing outside of the class.




          Edit
          As you had trouble getting the unique_ptr running, here's what works for me.



          Modify the node to use unique_ptr:



          struct node
          {
          int val;
          std::unique_ptr<node> left;
          std::unique_ptr<node> right;

          // c'tor left out
          };


          Also, use a unique_ptr for root. You'll receive heavy compiler complaints,
          because a unique_ptr cannot be simply copied (one of the instances must go invalid, as it's unique). I suggest altering the private functions:



          void _insert(std::unique_ptr<node>& curr, int val);
          void _del(std::unique_ptr<node>& curr, int val);


          Sample implementation for _insert:



          void Bst::_insert(std::unique_ptr<node>& curr, int val)
          {
          if (!curr)
          {
          curr = std::make_unique<node>(val);
          return;
          }

          if (val < curr->val)
          {
          _insert(curr->left, val);
          }
          else
          {
          _insert(curr->right, val);
          }
          }


          Your public insert method would be something like:



          void insert(int val)
          {
          _insert(root, val);
          }


          Also: add a find function to see if an element is in the tree. Use the find function to write some simple tests. You may rename del to erase to be closer at the standard naming.





          Edit 2 Separate output from data storage:



          A friend function is relatively inflexible, as you cannot easily exchange the printing algorithm. You may implement iterators (hard) or provide a traverse function. I'd go for the second approach, as you already have the algorithm in _print: rename print to traverse and pass it a function pointer, then replace the output to std::cout by a call to the function pointer.




          class Bst
          {
          ...
          // traverse with function pointer
          void traverse(void (*func)(int));
          };

          int main()
          {
          Bst b;
          // insert data ...

          b.traverse((auto v){ std::cout << v << " "; });
          std::cout << "n";
          }






          share|improve this answer










          New contributor




          Cornholio is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.






          $endgroup$













          • $begingroup$
            I am doing "using std::cin"(similarly for others) and not "using namespace std". Is it ok ? Also, I thought of using unique_ptr but in that case how do i pass arguments in recursion as it is not allowed for unqiue_ptr ?
            $endgroup$
            – bornfree
            yesterday












          • $begingroup$
            You shouldn't do so in header files as you cannot tell if you/someone else wants to use identifiers as cin or shared_ptr. In implementation files it's kind of ok, though I would strongly advice against doing. At best it looks funny. Everybody else will think it is something different than an STL template, so you're reducing the readability of your code.
            $endgroup$
            – Cornholio
            yesterday










          • $begingroup$
            Thanks for your valuable review. Regarding your fourth point of making print a separate function. Since, root is a private member of Bst class, I will have to create a friend function so that print can access the nodes ?
            $endgroup$
            – bornfree
            22 hours ago



















          5












          $begingroup$


          • Separate your class declaration and definition into separate Bst.h and Bst.cpp files

          • For Bst.h provide header guards

          • initialize shared_ptr<node> member, root with nullptr






          share|improve this answer








          New contributor




          programmer is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
          Check out our Code of Conduct.






          $endgroup$









          • 2




            $begingroup$
            I thought root will be initialized to nullptr by default ?
            $endgroup$
            – bornfree
            yesterday



















          0












          $begingroup$

          You forgot to include any of the headers that your code depends on; copy/paste of this into https://godbolt.org/ gives lots of errors until I add #include <memory> and iostream, and some using declarations (https://godbolt.org/z/cE0GDR). So the code in the question is incomplete.



          More importantly, for shared_ptr to be thread-safe, it has to use atomic-increment / decrement instructions, and check if the ref-count has become 0 after it's done referencing a node. This makes it significantly more expensive than I think you want. As another answer suggests, std::unique_ptr<> is probably a better choice.



          So for example, your findmin() source looks really simple, just traversing down to the left-most leaf node, but because you're using shared_ptr it compiles (on x86-64) to lock add and lock xadd instructions, potentially calling the destructor to free the node.



          (gcc emits checks before actually doing the atomic ++ / -- ref counting. It checks the address of a weak alias for __pthread_key_create. I think this might just be checking if the program could possibly be multi-threaded, i.e. linked with the thread library, rather than if multiple threads are currently running. So some of the cost of shared_ptr might go away in some single-threaded programs.)



          If you're looking at the asm on the Godbolt compiler explorer, search for the lock prefix for x86-64. Or for AArch64, look for ldaxr/stlxr LL/SC retry loops (load-acquire exclusive / store sequential-release exclusive).






          share|improve this answer









          $endgroup$













            Your Answer





            StackExchange.ifUsing("editor", function () {
            return StackExchange.using("mathjaxEditing", function () {
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            });
            });
            }, "mathjax-editing");

            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "196"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });






            bornfree is a new contributor. Be nice, and check out our Code of Conduct.










            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f212692%2fbinary-search-tree-implementation-using-smart-pointers%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            3 Answers
            3






            active

            oldest

            votes








            3 Answers
            3






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            9












            $begingroup$


            • please do not using namespace std;

            • why has Bst::findmin no prefix _?

            • why do you use std::shared_ptr<> at all? std::shared_ptr is ment to represent shared ownership. This makes no sense for tree nodes, you're not going to have two trees with the same nodes. std::unique_ptr<> is a better candidate.

            • it is generally good to separate the data structure from its serialization. Make print a separate function and also handle the member printing outside of the class.




            Edit
            As you had trouble getting the unique_ptr running, here's what works for me.



            Modify the node to use unique_ptr:



            struct node
            {
            int val;
            std::unique_ptr<node> left;
            std::unique_ptr<node> right;

            // c'tor left out
            };


            Also, use a unique_ptr for root. You'll receive heavy compiler complaints,
            because a unique_ptr cannot be simply copied (one of the instances must go invalid, as it's unique). I suggest altering the private functions:



            void _insert(std::unique_ptr<node>& curr, int val);
            void _del(std::unique_ptr<node>& curr, int val);


            Sample implementation for _insert:



            void Bst::_insert(std::unique_ptr<node>& curr, int val)
            {
            if (!curr)
            {
            curr = std::make_unique<node>(val);
            return;
            }

            if (val < curr->val)
            {
            _insert(curr->left, val);
            }
            else
            {
            _insert(curr->right, val);
            }
            }


            Your public insert method would be something like:



            void insert(int val)
            {
            _insert(root, val);
            }


            Also: add a find function to see if an element is in the tree. Use the find function to write some simple tests. You may rename del to erase to be closer at the standard naming.





            Edit 2 Separate output from data storage:



            A friend function is relatively inflexible, as you cannot easily exchange the printing algorithm. You may implement iterators (hard) or provide a traverse function. I'd go for the second approach, as you already have the algorithm in _print: rename print to traverse and pass it a function pointer, then replace the output to std::cout by a call to the function pointer.




            class Bst
            {
            ...
            // traverse with function pointer
            void traverse(void (*func)(int));
            };

            int main()
            {
            Bst b;
            // insert data ...

            b.traverse((auto v){ std::cout << v << " "; });
            std::cout << "n";
            }






            share|improve this answer










            New contributor




            Cornholio is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.






            $endgroup$













            • $begingroup$
              I am doing "using std::cin"(similarly for others) and not "using namespace std". Is it ok ? Also, I thought of using unique_ptr but in that case how do i pass arguments in recursion as it is not allowed for unqiue_ptr ?
              $endgroup$
              – bornfree
              yesterday












            • $begingroup$
              You shouldn't do so in header files as you cannot tell if you/someone else wants to use identifiers as cin or shared_ptr. In implementation files it's kind of ok, though I would strongly advice against doing. At best it looks funny. Everybody else will think it is something different than an STL template, so you're reducing the readability of your code.
              $endgroup$
              – Cornholio
              yesterday










            • $begingroup$
              Thanks for your valuable review. Regarding your fourth point of making print a separate function. Since, root is a private member of Bst class, I will have to create a friend function so that print can access the nodes ?
              $endgroup$
              – bornfree
              22 hours ago
















            9












            $begingroup$


            • please do not using namespace std;

            • why has Bst::findmin no prefix _?

            • why do you use std::shared_ptr<> at all? std::shared_ptr is ment to represent shared ownership. This makes no sense for tree nodes, you're not going to have two trees with the same nodes. std::unique_ptr<> is a better candidate.

            • it is generally good to separate the data structure from its serialization. Make print a separate function and also handle the member printing outside of the class.




            Edit
            As you had trouble getting the unique_ptr running, here's what works for me.



            Modify the node to use unique_ptr:



            struct node
            {
            int val;
            std::unique_ptr<node> left;
            std::unique_ptr<node> right;

            // c'tor left out
            };


            Also, use a unique_ptr for root. You'll receive heavy compiler complaints,
            because a unique_ptr cannot be simply copied (one of the instances must go invalid, as it's unique). I suggest altering the private functions:



            void _insert(std::unique_ptr<node>& curr, int val);
            void _del(std::unique_ptr<node>& curr, int val);


            Sample implementation for _insert:



            void Bst::_insert(std::unique_ptr<node>& curr, int val)
            {
            if (!curr)
            {
            curr = std::make_unique<node>(val);
            return;
            }

            if (val < curr->val)
            {
            _insert(curr->left, val);
            }
            else
            {
            _insert(curr->right, val);
            }
            }


            Your public insert method would be something like:



            void insert(int val)
            {
            _insert(root, val);
            }


            Also: add a find function to see if an element is in the tree. Use the find function to write some simple tests. You may rename del to erase to be closer at the standard naming.





            Edit 2 Separate output from data storage:



            A friend function is relatively inflexible, as you cannot easily exchange the printing algorithm. You may implement iterators (hard) or provide a traverse function. I'd go for the second approach, as you already have the algorithm in _print: rename print to traverse and pass it a function pointer, then replace the output to std::cout by a call to the function pointer.




            class Bst
            {
            ...
            // traverse with function pointer
            void traverse(void (*func)(int));
            };

            int main()
            {
            Bst b;
            // insert data ...

            b.traverse((auto v){ std::cout << v << " "; });
            std::cout << "n";
            }






            share|improve this answer










            New contributor




            Cornholio is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.






            $endgroup$













            • $begingroup$
              I am doing "using std::cin"(similarly for others) and not "using namespace std". Is it ok ? Also, I thought of using unique_ptr but in that case how do i pass arguments in recursion as it is not allowed for unqiue_ptr ?
              $endgroup$
              – bornfree
              yesterday












            • $begingroup$
              You shouldn't do so in header files as you cannot tell if you/someone else wants to use identifiers as cin or shared_ptr. In implementation files it's kind of ok, though I would strongly advice against doing. At best it looks funny. Everybody else will think it is something different than an STL template, so you're reducing the readability of your code.
              $endgroup$
              – Cornholio
              yesterday










            • $begingroup$
              Thanks for your valuable review. Regarding your fourth point of making print a separate function. Since, root is a private member of Bst class, I will have to create a friend function so that print can access the nodes ?
              $endgroup$
              – bornfree
              22 hours ago














            9












            9








            9





            $begingroup$


            • please do not using namespace std;

            • why has Bst::findmin no prefix _?

            • why do you use std::shared_ptr<> at all? std::shared_ptr is ment to represent shared ownership. This makes no sense for tree nodes, you're not going to have two trees with the same nodes. std::unique_ptr<> is a better candidate.

            • it is generally good to separate the data structure from its serialization. Make print a separate function and also handle the member printing outside of the class.




            Edit
            As you had trouble getting the unique_ptr running, here's what works for me.



            Modify the node to use unique_ptr:



            struct node
            {
            int val;
            std::unique_ptr<node> left;
            std::unique_ptr<node> right;

            // c'tor left out
            };


            Also, use a unique_ptr for root. You'll receive heavy compiler complaints,
            because a unique_ptr cannot be simply copied (one of the instances must go invalid, as it's unique). I suggest altering the private functions:



            void _insert(std::unique_ptr<node>& curr, int val);
            void _del(std::unique_ptr<node>& curr, int val);


            Sample implementation for _insert:



            void Bst::_insert(std::unique_ptr<node>& curr, int val)
            {
            if (!curr)
            {
            curr = std::make_unique<node>(val);
            return;
            }

            if (val < curr->val)
            {
            _insert(curr->left, val);
            }
            else
            {
            _insert(curr->right, val);
            }
            }


            Your public insert method would be something like:



            void insert(int val)
            {
            _insert(root, val);
            }


            Also: add a find function to see if an element is in the tree. Use the find function to write some simple tests. You may rename del to erase to be closer at the standard naming.





            Edit 2 Separate output from data storage:



            A friend function is relatively inflexible, as you cannot easily exchange the printing algorithm. You may implement iterators (hard) or provide a traverse function. I'd go for the second approach, as you already have the algorithm in _print: rename print to traverse and pass it a function pointer, then replace the output to std::cout by a call to the function pointer.




            class Bst
            {
            ...
            // traverse with function pointer
            void traverse(void (*func)(int));
            };

            int main()
            {
            Bst b;
            // insert data ...

            b.traverse((auto v){ std::cout << v << " "; });
            std::cout << "n";
            }






            share|improve this answer










            New contributor




            Cornholio is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.






            $endgroup$




            • please do not using namespace std;

            • why has Bst::findmin no prefix _?

            • why do you use std::shared_ptr<> at all? std::shared_ptr is ment to represent shared ownership. This makes no sense for tree nodes, you're not going to have two trees with the same nodes. std::unique_ptr<> is a better candidate.

            • it is generally good to separate the data structure from its serialization. Make print a separate function and also handle the member printing outside of the class.




            Edit
            As you had trouble getting the unique_ptr running, here's what works for me.



            Modify the node to use unique_ptr:



            struct node
            {
            int val;
            std::unique_ptr<node> left;
            std::unique_ptr<node> right;

            // c'tor left out
            };


            Also, use a unique_ptr for root. You'll receive heavy compiler complaints,
            because a unique_ptr cannot be simply copied (one of the instances must go invalid, as it's unique). I suggest altering the private functions:



            void _insert(std::unique_ptr<node>& curr, int val);
            void _del(std::unique_ptr<node>& curr, int val);


            Sample implementation for _insert:



            void Bst::_insert(std::unique_ptr<node>& curr, int val)
            {
            if (!curr)
            {
            curr = std::make_unique<node>(val);
            return;
            }

            if (val < curr->val)
            {
            _insert(curr->left, val);
            }
            else
            {
            _insert(curr->right, val);
            }
            }


            Your public insert method would be something like:



            void insert(int val)
            {
            _insert(root, val);
            }


            Also: add a find function to see if an element is in the tree. Use the find function to write some simple tests. You may rename del to erase to be closer at the standard naming.





            Edit 2 Separate output from data storage:



            A friend function is relatively inflexible, as you cannot easily exchange the printing algorithm. You may implement iterators (hard) or provide a traverse function. I'd go for the second approach, as you already have the algorithm in _print: rename print to traverse and pass it a function pointer, then replace the output to std::cout by a call to the function pointer.




            class Bst
            {
            ...
            // traverse with function pointer
            void traverse(void (*func)(int));
            };

            int main()
            {
            Bst b;
            // insert data ...

            b.traverse((auto v){ std::cout << v << " "; });
            std::cout << "n";
            }







            share|improve this answer










            New contributor




            Cornholio is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.









            share|improve this answer



            share|improve this answer








            edited 18 hours ago





















            New contributor




            Cornholio is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.









            answered yesterday









            CornholioCornholio

            4616




            4616




            New contributor




            Cornholio is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.





            New contributor





            Cornholio is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.






            Cornholio is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.












            • $begingroup$
              I am doing "using std::cin"(similarly for others) and not "using namespace std". Is it ok ? Also, I thought of using unique_ptr but in that case how do i pass arguments in recursion as it is not allowed for unqiue_ptr ?
              $endgroup$
              – bornfree
              yesterday












            • $begingroup$
              You shouldn't do so in header files as you cannot tell if you/someone else wants to use identifiers as cin or shared_ptr. In implementation files it's kind of ok, though I would strongly advice against doing. At best it looks funny. Everybody else will think it is something different than an STL template, so you're reducing the readability of your code.
              $endgroup$
              – Cornholio
              yesterday










            • $begingroup$
              Thanks for your valuable review. Regarding your fourth point of making print a separate function. Since, root is a private member of Bst class, I will have to create a friend function so that print can access the nodes ?
              $endgroup$
              – bornfree
              22 hours ago


















            • $begingroup$
              I am doing "using std::cin"(similarly for others) and not "using namespace std". Is it ok ? Also, I thought of using unique_ptr but in that case how do i pass arguments in recursion as it is not allowed for unqiue_ptr ?
              $endgroup$
              – bornfree
              yesterday












            • $begingroup$
              You shouldn't do so in header files as you cannot tell if you/someone else wants to use identifiers as cin or shared_ptr. In implementation files it's kind of ok, though I would strongly advice against doing. At best it looks funny. Everybody else will think it is something different than an STL template, so you're reducing the readability of your code.
              $endgroup$
              – Cornholio
              yesterday










            • $begingroup$
              Thanks for your valuable review. Regarding your fourth point of making print a separate function. Since, root is a private member of Bst class, I will have to create a friend function so that print can access the nodes ?
              $endgroup$
              – bornfree
              22 hours ago
















            $begingroup$
            I am doing "using std::cin"(similarly for others) and not "using namespace std". Is it ok ? Also, I thought of using unique_ptr but in that case how do i pass arguments in recursion as it is not allowed for unqiue_ptr ?
            $endgroup$
            – bornfree
            yesterday






            $begingroup$
            I am doing "using std::cin"(similarly for others) and not "using namespace std". Is it ok ? Also, I thought of using unique_ptr but in that case how do i pass arguments in recursion as it is not allowed for unqiue_ptr ?
            $endgroup$
            – bornfree
            yesterday














            $begingroup$
            You shouldn't do so in header files as you cannot tell if you/someone else wants to use identifiers as cin or shared_ptr. In implementation files it's kind of ok, though I would strongly advice against doing. At best it looks funny. Everybody else will think it is something different than an STL template, so you're reducing the readability of your code.
            $endgroup$
            – Cornholio
            yesterday




            $begingroup$
            You shouldn't do so in header files as you cannot tell if you/someone else wants to use identifiers as cin or shared_ptr. In implementation files it's kind of ok, though I would strongly advice against doing. At best it looks funny. Everybody else will think it is something different than an STL template, so you're reducing the readability of your code.
            $endgroup$
            – Cornholio
            yesterday












            $begingroup$
            Thanks for your valuable review. Regarding your fourth point of making print a separate function. Since, root is a private member of Bst class, I will have to create a friend function so that print can access the nodes ?
            $endgroup$
            – bornfree
            22 hours ago




            $begingroup$
            Thanks for your valuable review. Regarding your fourth point of making print a separate function. Since, root is a private member of Bst class, I will have to create a friend function so that print can access the nodes ?
            $endgroup$
            – bornfree
            22 hours ago













            5












            $begingroup$


            • Separate your class declaration and definition into separate Bst.h and Bst.cpp files

            • For Bst.h provide header guards

            • initialize shared_ptr<node> member, root with nullptr






            share|improve this answer








            New contributor




            programmer is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.






            $endgroup$









            • 2




              $begingroup$
              I thought root will be initialized to nullptr by default ?
              $endgroup$
              – bornfree
              yesterday
















            5












            $begingroup$


            • Separate your class declaration and definition into separate Bst.h and Bst.cpp files

            • For Bst.h provide header guards

            • initialize shared_ptr<node> member, root with nullptr






            share|improve this answer








            New contributor




            programmer is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.






            $endgroup$









            • 2




              $begingroup$
              I thought root will be initialized to nullptr by default ?
              $endgroup$
              – bornfree
              yesterday














            5












            5








            5





            $begingroup$


            • Separate your class declaration and definition into separate Bst.h and Bst.cpp files

            • For Bst.h provide header guards

            • initialize shared_ptr<node> member, root with nullptr






            share|improve this answer








            New contributor




            programmer is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.






            $endgroup$




            • Separate your class declaration and definition into separate Bst.h and Bst.cpp files

            • For Bst.h provide header guards

            • initialize shared_ptr<node> member, root with nullptr







            share|improve this answer








            New contributor




            programmer is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.









            share|improve this answer



            share|improve this answer






            New contributor




            programmer is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.









            answered yesterday









            programmerprogrammer

            513




            513




            New contributor




            programmer is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.





            New contributor





            programmer is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.






            programmer is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
            Check out our Code of Conduct.








            • 2




              $begingroup$
              I thought root will be initialized to nullptr by default ?
              $endgroup$
              – bornfree
              yesterday














            • 2




              $begingroup$
              I thought root will be initialized to nullptr by default ?
              $endgroup$
              – bornfree
              yesterday








            2




            2




            $begingroup$
            I thought root will be initialized to nullptr by default ?
            $endgroup$
            – bornfree
            yesterday




            $begingroup$
            I thought root will be initialized to nullptr by default ?
            $endgroup$
            – bornfree
            yesterday











            0












            $begingroup$

            You forgot to include any of the headers that your code depends on; copy/paste of this into https://godbolt.org/ gives lots of errors until I add #include <memory> and iostream, and some using declarations (https://godbolt.org/z/cE0GDR). So the code in the question is incomplete.



            More importantly, for shared_ptr to be thread-safe, it has to use atomic-increment / decrement instructions, and check if the ref-count has become 0 after it's done referencing a node. This makes it significantly more expensive than I think you want. As another answer suggests, std::unique_ptr<> is probably a better choice.



            So for example, your findmin() source looks really simple, just traversing down to the left-most leaf node, but because you're using shared_ptr it compiles (on x86-64) to lock add and lock xadd instructions, potentially calling the destructor to free the node.



            (gcc emits checks before actually doing the atomic ++ / -- ref counting. It checks the address of a weak alias for __pthread_key_create. I think this might just be checking if the program could possibly be multi-threaded, i.e. linked with the thread library, rather than if multiple threads are currently running. So some of the cost of shared_ptr might go away in some single-threaded programs.)



            If you're looking at the asm on the Godbolt compiler explorer, search for the lock prefix for x86-64. Or for AArch64, look for ldaxr/stlxr LL/SC retry loops (load-acquire exclusive / store sequential-release exclusive).






            share|improve this answer









            $endgroup$


















              0












              $begingroup$

              You forgot to include any of the headers that your code depends on; copy/paste of this into https://godbolt.org/ gives lots of errors until I add #include <memory> and iostream, and some using declarations (https://godbolt.org/z/cE0GDR). So the code in the question is incomplete.



              More importantly, for shared_ptr to be thread-safe, it has to use atomic-increment / decrement instructions, and check if the ref-count has become 0 after it's done referencing a node. This makes it significantly more expensive than I think you want. As another answer suggests, std::unique_ptr<> is probably a better choice.



              So for example, your findmin() source looks really simple, just traversing down to the left-most leaf node, but because you're using shared_ptr it compiles (on x86-64) to lock add and lock xadd instructions, potentially calling the destructor to free the node.



              (gcc emits checks before actually doing the atomic ++ / -- ref counting. It checks the address of a weak alias for __pthread_key_create. I think this might just be checking if the program could possibly be multi-threaded, i.e. linked with the thread library, rather than if multiple threads are currently running. So some of the cost of shared_ptr might go away in some single-threaded programs.)



              If you're looking at the asm on the Godbolt compiler explorer, search for the lock prefix for x86-64. Or for AArch64, look for ldaxr/stlxr LL/SC retry loops (load-acquire exclusive / store sequential-release exclusive).






              share|improve this answer









              $endgroup$
















                0












                0








                0





                $begingroup$

                You forgot to include any of the headers that your code depends on; copy/paste of this into https://godbolt.org/ gives lots of errors until I add #include <memory> and iostream, and some using declarations (https://godbolt.org/z/cE0GDR). So the code in the question is incomplete.



                More importantly, for shared_ptr to be thread-safe, it has to use atomic-increment / decrement instructions, and check if the ref-count has become 0 after it's done referencing a node. This makes it significantly more expensive than I think you want. As another answer suggests, std::unique_ptr<> is probably a better choice.



                So for example, your findmin() source looks really simple, just traversing down to the left-most leaf node, but because you're using shared_ptr it compiles (on x86-64) to lock add and lock xadd instructions, potentially calling the destructor to free the node.



                (gcc emits checks before actually doing the atomic ++ / -- ref counting. It checks the address of a weak alias for __pthread_key_create. I think this might just be checking if the program could possibly be multi-threaded, i.e. linked with the thread library, rather than if multiple threads are currently running. So some of the cost of shared_ptr might go away in some single-threaded programs.)



                If you're looking at the asm on the Godbolt compiler explorer, search for the lock prefix for x86-64. Or for AArch64, look for ldaxr/stlxr LL/SC retry loops (load-acquire exclusive / store sequential-release exclusive).






                share|improve this answer









                $endgroup$



                You forgot to include any of the headers that your code depends on; copy/paste of this into https://godbolt.org/ gives lots of errors until I add #include <memory> and iostream, and some using declarations (https://godbolt.org/z/cE0GDR). So the code in the question is incomplete.



                More importantly, for shared_ptr to be thread-safe, it has to use atomic-increment / decrement instructions, and check if the ref-count has become 0 after it's done referencing a node. This makes it significantly more expensive than I think you want. As another answer suggests, std::unique_ptr<> is probably a better choice.



                So for example, your findmin() source looks really simple, just traversing down to the left-most leaf node, but because you're using shared_ptr it compiles (on x86-64) to lock add and lock xadd instructions, potentially calling the destructor to free the node.



                (gcc emits checks before actually doing the atomic ++ / -- ref counting. It checks the address of a weak alias for __pthread_key_create. I think this might just be checking if the program could possibly be multi-threaded, i.e. linked with the thread library, rather than if multiple threads are currently running. So some of the cost of shared_ptr might go away in some single-threaded programs.)



                If you're looking at the asm on the Godbolt compiler explorer, search for the lock prefix for x86-64. Or for AArch64, look for ldaxr/stlxr LL/SC retry loops (load-acquire exclusive / store sequential-release exclusive).







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered 20 hours ago









                Peter CordesPeter Cordes

                1,409516




                1,409516






















                    bornfree is a new contributor. Be nice, and check out our Code of Conduct.










                    draft saved

                    draft discarded


















                    bornfree is a new contributor. Be nice, and check out our Code of Conduct.













                    bornfree is a new contributor. Be nice, and check out our Code of Conduct.












                    bornfree is a new contributor. Be nice, and check out our Code of Conduct.
















                    Thanks for contributing an answer to Code Review Stack Exchange!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    Use MathJax to format equations. MathJax reference.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f212692%2fbinary-search-tree-implementation-using-smart-pointers%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    "Incorrect syntax near the keyword 'ON'. (on update cascade, on delete cascade,)

                    Alcedinidae

                    RAC Tourist Trophy