Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I avoid reference to pointer in modern C++ or it is okay in this special case

I'm aware that raw pointers (or more accurately new and delete) in modern C++ must be avoided unless really needed. I believe when it comes to graph implementation, pointers are pretty useful. I'd be happy to hear comments on this though.

I'm playing around with Binary Search Trees and writing insert functions. I implemented using two different but similar methods, one using pointer and the other one using reference to pointer. Clearly I would like to change the the pointer itself (ref to ptr). Wondering if any of them has advantages or either should not be used and must be written using const reference or smart pointer (modern way).

Node *insert_ptr(Node *root, int data)
{
    if (root == NULL)
        return create(data);
    else if (data < root->data)
        root->left = insert(root->left, data);
    else
        root->right = insert(root->right, data);
    return root;
}

void insert_ref_to_ptr(Node *&root, int data)
{
    if (root == NULL)
        root = create(data);
    else if (data < root->data)
        insert(root->left, data);
    else
        insert(root->right, data);
}

I'm providing the rest of code below if you want to get a run. You may want to use 1 3 6 8 10 14 13 4 7 as input

struct Node
{
    int data;
    Node *left;
    Node *right;
};

void display(Node *root)
{
    if (root != NULL)
    {
        display(root->left);
        cout << root->data << " ";
        display(root->right);
    }
}

Node *create(int data)
{
    Node *node = new Node();
    node->data = data;
    node->left = node->right = NULL;
    return node;
}

int main()
{
    Node *root = NULL;
    vector<int> nodes;

    string line;
    int value;
    getline(cin, line);
    stringstream split(line);
    while (split >> value)
        nodes.push_back(value);
    /*
    root = insert_ptr(root, nodes[0]);
    for (int i = 1; i < nodes.size(); i++)
        insert_ptr(root, nodes[i]);
    */

    for (int i = 0; i < nodes.size(); i++)
        insert_ref_to_ptr(root, nodes[i]);
    display(root);
    return 0;
}

like image 859
Karim Sole Avatar asked Feb 03 '19 19:02

Karim Sole


People also ask

Is it better to use pointer or reference?

References are usually preferred over pointers whenever you don't need “reseating”. This usually means that references are most useful in a class's public interface. References typically appear on the skin of an object, and pointers on the inside.

Should pointers be avoided?

It is best to avoid using pointers in C++ as much as possible. The use of pointers can lead to confusion of ownership which can directly or indirectly lead to memory leaks.

Why are references better than pointers?

References are used to refer an existing variable in another name whereas pointers are used to store address of variable. References cannot have a null value assigned but pointer can. A reference variable can be referenced by pass by value whereas a pointer can be referenced but pass by reference.

Are pointers safer than references?

References are Safer and Easier to Use References don't have to be dereferenced like pointers and can be used like normal variables (as an alias for some other variable). It is necessary to pass objects as a reference in a copy constructor.


2 Answers

that pointers in modern C++ must be avoided unless really needed that's wrong.

new and delete should be avoided and you should use unique and shared pointers instead.

But you will still pass an object by reference or raw pointer if you don't transfer ownership.

Your Node has an ownership relation so it should be:

struct Node
{
    int data;
    std::unique_ptr<Node> left;
    std::unique_ptr<Node> right;
};

or

struct Node
{
    int data;
    std::shared_ptr<Node> left;
    std::shared_ptr<Node> right;
};

Your display function does not need ownership, so you pass the node as pointer (or reference)

void display(Node *root)
{
    if (root != nullptr)
    {
        display(root->left);
        cout << root->data << " ";
        display(root->right);
    }
}

As you don't plan to change it I would go with const ref:

void display(const Node &root)
{
    if(root.left != nullptr) {
        display(*root.left);
    }

    cout << root.data << " ";

    if(root.right != nullptr) {
        display(*root.right);
    }
}

You insert_ref_to_ptr is a really bad construct, because it is not clear that it transferes any ownership and as is not clear that it calls create(data) inside which create a Node using new.

The create would look something like that:

std::unique_ptr<Node> create(int data)
{
    auto node = std::make_unique<Node>();
    node->data = data;
    return std::move(node);
}

And the insert_ref_to_ptr function something like that:

void insert_ref_to_ptr(std::unique_ptr<Node> &root, int data)
{
    if (root == nullptr)
        root = std::move(create(data));
    else if (data < root->data)
        insert(root->left, data);
    else
        insert(root->right, data);
}
like image 126
t.niese Avatar answered Oct 22 '22 20:10

t.niese


I'm aware that pointers in modern C++ must be avoided unless really needed.

That is not true. What you probably heard is that you should avoid raw (naked) pointers if your requirements fit an standardized pattern/solution, like unique_ptr.

Clearly I would like to change the the pointer itself (ref to ptr). Wondering if any of them has advantages or either should not be used and must be written using const reference (modern way).

If you want to have output parameters, you have quite some ways to do it:

  • Return value.
  • Reference to value (as parameter).
  • Pointer to value (as parameter).
  • Several return values in a struct, std::tuple or using structured bindings.
  • Member variable (only for member functions in classes).
  • Global variable.

There is not a single best way for all cases. Which one you use is up to you and how you are going to use the function. In some cases, it also depends on the coding style guidelines of the projects (e.g. in the case of reference vs. pointer). For instance:

  • Some people prefer to use a return value if there is only a single output parameter.
  • Some libraries prefer to use return values exclusively for error codes.
  • Some coding style guidelines avoid references because they want to see explicitly in the caller that you are passing an address (e.g. f(a) vs. f(&a)).
  • In member functions you usually employ the member variables.
  • In almost all cases you should avoid global variables for this purpose.

must be written using const reference (modern way).

By the way, note that you didn't write a const reference, just a reference to Note *. And it is not "modern C++": references were a part of the first C++ standard.

like image 36
Acorn Avatar answered Oct 22 '22 21:10

Acorn