0

This is the header of my generic binary search tree. For now I'm just using a raw pointer for the root of the tree. What kind of pointer should I use? Some of the types are unique,shared,(smart pointers) weak , and raw and on and on. . . . .

template <typename T>
class BST
{
public:
    BST();
    BSTNode<T>* Root;
    void Insert(BSTNode<T> newNode);
    void Delete(T deleteNode);
    BSTNode<T>* ReturnNodeSearch();
    BSTNode<T>* MinimumValue();
    BSTNode<T>* MaximumValue();
    bool isEmpty();
};
Person
  • 15
  • 2
  • What do you mean "what kind of pointer"? Do you mean should I use a shared_ptr > or similar? – Daniel Feb 10 '18 at 23:50
  • 2
    First, are you sure you need a pointer at all? Is there a reason you can't just have a direct `BSTNode Root;` member? – aschepler Feb 10 '18 at 23:51
  • 3
    @Cppplus1 I hope that was meant as a joke, because that is terrible advice. – Arnav Borborah Feb 10 '18 at 23:53
  • @Cppplus1 **"... if you never have pointer errors. "** - You have very high expectations. People don't just initially write code that works the first time, especially w/ pointers. And if you are concerned about the memory overhead/performance of smart pointers over raw pointers, you should probably know that `std::unique_ptr` only has overhead when using a non-trivial deleter, otherwise it is pretty much the same as a normal pointer. Here is a good answer that explains this: https://stackoverflow.com/questions/22295665/how-much-is-the-overhead-of-smart-pointers-compared-to-normal-pointers-in-c – Arnav Borborah Feb 10 '18 at 23:57
  • 3
    There are ready made [containers](http://en.cppreference.com/w/cpp/container) in C++. Use those. There are no containers in C so people need to wire the structs together like that. – Ron Feb 10 '18 at 23:59
  • 1
    Use a `std::unique_ptr` ... unless you want copies of your tree to point at exactly the same nodes (extremely unlikely). – Galik Feb 11 '18 at 00:06
  • What do the `BSTNode`s use to connect with their child nodes? You don't gain much from automating the destruction of `Root` if you still have to manually delete all of the nodes under `Root` – user4581301 Feb 11 '18 at 00:36
  • I made an edit to my original answer. – Francis Cugler Feb 11 '18 at 01:14

3 Answers3

2

Use a std::unique_ptr as it is very unlikely you want two separate BST objects to share the same implementation nodes. Usually, in that case, you would just use an external reference or (possibly) an external std::shared_ptr to the BST object itself.

Galik
  • 47,303
  • 4
  • 80
  • 117
0

It just depends on what you want to do. I would suggest that best practice would be to use either std::unique_ptr<> or std::shared_ptr<> to make sure the memory is properly released when no longer needed. However, a raw pointer can work here, although you will have to handle the deallocation by yourself. In general, the benefits of smart pointers for handling and owning dynamically allocated memory tend to outweigh the benefits of using raw pointers.

In more detail but at a high level:

  • Raw pointer -- handles the problem but you just have to manage deallocation if you no longer need this memory anymore. You could accidentally deallocate the memory in one object/function when some other object still has a pointer to it
  • std::unique_ptr<> -- will manage the memory over its lifetime, good if you don't have to share the pointer with any other object
  • std::shared_ptr<> -- also will manage the memory, adds a bit of overhead by reference counting how many std::shared_ptr<>s also are watching the memory location. This will also make sure the memory is only removed once no other std::shared_ptr<> object is pointing to it
  • std::weak_ptr<> -- can only be used in combination with std::shared_ptr<>, used to prevent reference cycles for the same object in memory

There isn't necessarily a wrong answer. It just depends on your desired implementation. The only type you should never use is std::auto_ptr<>, so avoid that type.

Daniel
  • 1,291
  • 6
  • 15
  • 1
    I wouldn't suggest using raw pointer over smart pointer. Raw pointer should be used as an observer nowadays, not memory owner. – Zereges Feb 11 '18 at 00:17
  • It seems that many frown at the usage of raw pointers although they are a valid part of the language. I understand that they are hard to manage and can cause headaches when improperly used; however I disagree with the idea that one should `never` use them. To me it seems more like a cop-out and an excuse. When someone devotes themselves to the craft to be a professional I think that it is vital to know how to use all aspects of the language; otherwise what is the point. It's like a mathematician who doesn't learn Trigonometry... – Francis Cugler Feb 11 '18 at 00:25
  • 1
    @Zereges I agree, which is why I said that smart pointers were the best practice. But raw pointers still have their valid use cases, although I agree they are less important in more modern C++. – Daniel Feb 11 '18 at 00:25
  • @Galik I understand that; I was just making my statement above so that people new to the language do not get it in their minds to `always` avoid using or learning about `raw` pointers... – Francis Cugler Feb 11 '18 at 00:29
  • @Zereges I wasn't disagreeing with you; I mentioned something similar toward Galik; I was only stating that above so that those who are new to the language don't get it in their mind to complete avoid using or even learning raw pointers. – Francis Cugler Feb 11 '18 at 00:31
  • Sorry about the misconception of my comment: I was just making the statement not to disagree with another's comment but only to emphasize on the fact that when someone is new to the language that they shouldn't be discouraged towards `never` using `raw pointers` or even learning about them because they are a vital part of the language. – Francis Cugler Feb 11 '18 at 00:32
  • I modified the answer based on this discussion to better emphasize the use of smart pointers. – Daniel Feb 11 '18 at 00:35
-2

When looking at your code sample there are 3 options that come to mind where any of them could be viable options; let's explore these options below.

  • Raw pointer
  • shared_ptr<T>
  • unique_ptr<T>

Each of these have their own cons & pros in their usage.


Since you are using raw in your example; I'll start with that one first:

raw

  • Pro: - You have the flexibility to manage the memory your self, but it comes at the cost of a higher responsibility.
  • Con: - It is prone to memory leaks, dangling & invalid pointers.

shared_ptr

  • Pro: - Manages the memory for you; and is accessible across multiple objects as it is shared memory via reference counting.
  • Con: - It is based on the assumption that you will never face a memory leak or dangling pointer just because of using this smart pointer where it is not always guaranteed to be released but usually is in most cases. Sometimes you may not want a pointer to be accessible across multiple objects. It also has a small performance hit from reference counting.

unique_ptr

  • Pro: - Nearly same as above only that one object can own this providing more protection of unwanted access.
  • Con: - Similar as above under the assumption that the memory will always be freed. If the functionality needed does require multiple objects or sources to access this at some future point in time, then you are limited by using this type of pointer. You can transfer ownership but you can not access via multiple objects.

In the end it comes down to your particular need of which type you will want to use. In your particular situation there is nothing wrong with using raw pointers within the class if they are private members but you have more work to manage the class and more to be conscience of when dealing with the allocation & releasing of memory. If using unique_ptr then it is a matter of knowing if the memory object will be strictly internal to the pertaining class object or not. If using shared_ptr then it is a matter of knowing which external objects will be referencing it.


In the end you had asked:

What kind of pointer should I use?

Taking the knowledge from the above information and by understanding the type of object you are working with, let's consider what the BST does or is responsible for and its primary role as a class object.

We know that it is a Binary Space Partitioning Tree typically used with a Binary Search. Where one is the data structure and the other is the searching algorithm.

It is a tree that consists of a set of nodes and each node has 2 leaf nodes. The very first node in the tree is typically called the root or the head where a leaf node that has no data or is empty a terminating node is normally called the tail and is usually set to null or nullptr. We can see the relationship of these nodes and know that the BST will have ownership of at least the root node. This way each instance of a BST object will be unique from another. Example:

BST tree1; // Tree 1
BST tree2; // Tree 2

// Not valid code below but shown to illustrate a concept
tree1.root != tree2.root; // These two roots are not equal as in not the same memory.

This is what we would want to keep one tree unique from another and because of this behavior we really wouldn't want to use shared_ptr. With this particular class I think the better option here if not using raw pointers and managing your own memory and using smart pointers to show ownership and uniqueness between multiple objects that unique_ptr would then be the one to choose.


When designing classes and trying to decide which smart pointer to use these are the basic questions you should ask & answer yourself:

  • Does this object have a? If yes; does it solely own it or does each instance's internal memory need to be unique? If Yes; then use a unique_ptr
  • Does this object have to be referenced across multiple objects? If yes, then use shared_ptr.

Here are a few references: One from a paper and another from and Q/A, one from code review:

These references may also help you into making a well defined decision.

Community
  • 1
  • 1
Francis Cugler
  • 7,788
  • 2
  • 28
  • 59
  • Yup once again negative votes without a reason as to why... If those who voted negative would give a hint as to why they did; then I could go back and edit the answer to improve its quality and accuracy. Just down voting because you didn't like the answer is very destructive versus the idea of positive-criticism. – Francis Cugler Feb 11 '18 at 00:27
  • 1
    I down-voted because, even though this is a rather good information piece describing different types of pointer, it doesn't really address the question. The type of data structure (A BST tree) has internal nodes as an *implementation detail* so it is hard to imagine why a `shared_ptr` would be appropriate (*information hiding*). Also *raw pointer* is definitely not a recommended option for owning pointers (which this surely is). – Galik Feb 11 '18 at 00:32
  • @Galik Thank you for the positive-criticism as this gives me direction in how to improve my post. The OP did ask: `What kind of pointer should I use?` So I gave a break down of the 3 most popular use cases of pointers with their pros & cons; but now with your feedback I can edit my answer to reflect why `unique_ptr` would be the better choice. – Francis Cugler Feb 11 '18 at 00:35