how to design const member function, preventing it from modifying the object - c++

This a const member function, which allow me to get the minimum node of the tree:
BinarySearthTree* BinarySearchTree::min() const
{
// Return the minimum node (left-most node) value of the tree
BinarySearchTree * next = (BinarySearchTree *) this;
for (next ; next != NULL; next = next->pLeft)
if (next->pLeft == NULL)
return next;
}
I have to cast down the const-ness of 'this' pointer when assigning it to 'next', but this actually rises the potential that I may modify the value of what 'this' points to? Rather than always remind myself not to modify whatever 'next' points to, is there a way to prevent it happening by a better designing of the function?

Make next const:
const BinarySearthTree* BinarySearchTree::min() const
{
// Return the minimum node (left-most node) value of the tree
const BinarySearchTree *next;
for (next = this; next != NULL; next = next->pLeft)
if (next->pLeft == NULL)
return next;
return NULL;
}

If you don't want the contents to be modified, then you should make min() return a pointer to a const object.
Your next variable should also be a pointer to a const object, therefore.
Here is how I think your method should look:
const BinarySearchTree* BinarySearchTree::min() const
{
// Return the minimum node (left-most node) value of the tree
for (const BinarySearchTree* next = this; next != NULL; next = next->pLeft)
{
if (next->pLeft == NULL)
{
return next;
}
}
return this;
}
Also, in C++, you should avoid C-style casts. The const_cast exists for this purpose:
BinarySearchTree* next = const_cast<BinarySearchTree*>(this);
But that is not necessary in this instance.

Related

c++14 unique_ptr and make unique_ptr error use of deleted function 'std::unique-ptr'

I have a function like this..
unique_ptr<Node> test(unique_ptr<Node> &&node, string key)
{
if(!node)
{
return make_unique<Node>(key);
}
else
return node;
}
I want to create a node if the node is null, or to return the node. but it errors out saying "use of deleted function 'std::unique_ptr' ". What have I done wrong?
The problem is the way you are calling the function. But first of all you should accept your std::unique_ptr by value, not r-reference.
Then you need to std::move() your pointer when calling the function:
// accept by value
std::unique_ptr<Node> test(std::unique_ptr<Node> node)
{
if(!node)
return std::make_unique<Node>();
return node;
}
int main()
{
auto n = std::make_unique<Node>();
n = test(std::move(n)); // move the pointer
}
A std::unique_ptr can't be copied otherwise it would not be unique. You hav to move them.

Binary search tree. Pointer as reference parameter

So I am working on Binary search tree function. Why do I have to add a &sign in front of the node pointer? I thought it is already a pointer and it already points to a location. I understand that if I add a node, then I need to make sure the parent node point to the new node or the parent's node will still point to NULL. But why don't I have to do that if I pass my node pointer as node*&?
bool bst::remove123(int data, node*& x)
{
if (x == NULL)
{
return false;
}
else if (x->getData() < data)
{
return remove123(data, x->right);
}
else if (x->getData() > data)
{
return remove123(data, x->left);
}
else
{
node* old = x;
if (x->left == NULL)
{
x = x->right;
}
else if (x->right == NULL)
{
x = x->left;
}
else
{
replacement(old, x->left);
}
delete old;
return true;
}
}
Thank you
node*& x is a reference to a node*. This means that when bst::remove123 modifies x to point to a different address, the code that called bst::remove123 sees the same change in the node* variable that it passed to the method. If you declared the x parameter as node *x instead, then bst::remove123 would only be modifying a copy of the variable passed in that parameter, and those changes would be lost after the method returned. While the & is used to designate a reference, this is very different to the & operator (often used with pointers) which returns the address of the variable following it.
int n = 10;
int *pn = &n; // Create a pointer to int, set it to the address of n.
int& rn = n; // Create an int reference, set it to reference the same variable as n.
*pn = 5; // Set n to 5 via a dereferenced pn. A dereferencing operator *
// is needed to indicate that we want to change the memory that
// pn points to, not the address that the pointer contains.
rn = 20; // Set n to 20 via the reference rn. Unlike with pointers,
// references do not use a dereferencing operator.

C++ no known conversion for argument 1 from ‘Node*’ to ‘Node*&’

I am trying to use a recursive function to insert nodes into a binary search tree. However, when I try to call my function, I get the error message
no known conversion for argument 1 from ‘Node*’ to ‘Node*&’
This is the code for the recursive function and the function that calls it:
bool BST::add(int data)
{
if (root == NULL)
{
Node* gnu = new Node(data);
root = gnu;
return true;
}
return recursiveAdd(root, data);
}
bool BST::recursiveAdd(Node*& start, int data)
{
if (start == NULL)
{
Node* gnu = new Node(data);
start = gnu;
return true;
}
if (data < start->getData())
{
return recursiveAdd(start->getLeft(), data);
}
if (data > start->getData())
{
return recursiveAdd(start->getRight(), data);
}
if (data == start->getData())
{
return false;
}
return false;
}
The following are the functions for getLeft(), getRight(), and getData() from my Node class:
int Node::getData()
{
return data;
}
Node* Node::getLeft()
{
return leftChild;
}
Node* Node::getRight()
{
return rightChild;
}
I would really appreciate it if someone could show me what I need to fix for it to work properly.
Thanks!
You can't bind a temporary variable (the copies returned by getLeft and getRight) with a non-const lvalue reference, hence the error.
You shouldn't need to modify the pointer itself anyway. I'd suggest you rewrite your algorithm to work without this rather strange and error-prone requirement.
Your accessors return copies of the internal pointers
Node* Node::getLeft()
{
return leftChild;
}
Node* Node::getRight()
{
return rightChild;
}
But recursiveAdd needs to be able to modify the structure
start = gnu;
These approaches aren't compatible, and you were lucky the compiler caught you. You can either change getLeft() and getRight() to return references, or capture the structure change made by recursiveAdd() and propagate it using a mutator.
Node* theLeft = start->getLeft();
bool did_insert = recursiveAdd(theLeft, data);
start->setLeft(theLeft);
return did_insert;
Or, as Matt says, you can make recursiveAdd a friend and just do
return recursiveAdd(start->leftChild, data);
...
if (root == NULL)
{
Node* gnu = new Node(data);
root = gnu;
return true;
}
return recursiveAdd(root, data);
}
Block scope. If root is NULL, you're going to end up with no declaration for root outside of the block. This means that recursiveAdd() will be called with an invalid parameter; this cannot compile or else it would always have the opportunity to yield undefined behavior.
Regardless, since root will expire, so will the reference, as the reference will point to a temporary object. This is incorrect, as references cannot be bound to temporary objects--they, by definition, will point to "containers of values," often called lvalues in C++11 parlance.
There are also rvalues, but that is outside the scope of the question.

Passing a pointer to a function

I was trying to implement BST using C++ , so i tried this:
#include <iostream>
#include <stdlib.h>
struct node
{
int value;
node* left;
node* right;
};
void insert(node *cur , int val)
{
if(!cur)
{
cur = new node;
cur->value = val;
cur->left = NULL;
cur->right = NULL;
return;
}
if(val <= cur->value)
insert(cur->left , val);
else
insert(cur->right , val);
}
using namespace std;
int main()
{
node *root = NULL;
insert(root , 20);
insert(root , 21);
cout<<(*root).value;
return 0;
}
but I have a problem, my insert() function works good, but the change in cur does not seem to reflect into the root pointer, as root remains NULL after the `insert() function calls. What is wrong here?
EDIT: Thanks for all your answers, making a pointer to a pointer seems to be to be ugly and tedious, is there any other way around, acheiving this with some other design?
Here, the root itself has been passed to insert() using pass-by-value. so, from insert(), the value of root cannot be changed. In other words, the cur is local to insert() function. Any changes made to cur itself won't impact the actual argument passed.
If you want to change the value of root from insert(), you need to pass a pointer to root from main().
To elabotare, you can change the value at the address pointed by cur from insert(). So, following the same analogy, if you change
insert(&root , 20);
void insert(node **cur , int val)
all the occurrences of cur to *cur
you should be all good to go.
If you want the function to operate on the outside pointer rather than a local copy you need to pass by reference:
void insert(node*& cur, int val)
{
// ...
}
Otherwise the function works on a copy of the pointer and the outside variable remains unchanged.
The wrong is that you pas a pointer by value, change that value but the caller does not know about it. Change it to
void insert(node **cur , int val)
{
if(!*cur)
{
*cur = new node;
(*cur)->value = val;
(*cur)->left = NULL;
(*cur)->right = NULL;
return;
}
if(val <= (*cur)->value)
insert((*cur)->left , val);
else
insert((*cur)->right , val);
}
And change function call accordingly (...exercise!)
C++ makes function calls as Call by Value. So it makes a copy of the pointer and passes that to the function. If you pass that pointer, you have access to the data the pointer is pointing to, but NOT to the pointer outside the function itself, as only the adress the pointer is pointing to is copied.
You need to pass a pointer to the pointer if you want to modify the pointer itself (that would be the C attempt) or pass by reference, of which c++ is capable of.
If you want to use the C attempt:
void insert(node ** cur , int val)
if(!(*cur))
{
(*cur) = new node;
(*cur)->value = val;
(*cur)->left = NULL;
(*cur)->right = NULL;
return;
}
or the C++ attempt (here you only have to modify the type of cur, everthing else will remain as it is):
void insert(node *& cur , int val)
If you reassign cur to a new node in insert, that does not mean that root is assigned that value (especially that root is not an address at all, but NULL).
Either pass a pointer to an empty node to insert on initialization (and update it with relevant data) or return a new node (and assign it to root in main).

C++: where are my new nodes?

I am trying to add element in a Set using Binary Tree:
bool TreeSet::add(const string &str)
{
if (treesize == 0)
{
TreeNode->data = str;
treesize++;
return true;
}
else
{
if (str < TreeNode->data)
return insert(TreeNode->left, str);
else if (str > TreeNode->data)
return insert(TreeNode->right, str);
else
return false;
}
return false;
}
bool TreeSet::insert(TREE *node, const string &str) //private
{
if (node == NULL)
{
node = new TREE;
node->data=str;
node->left = NULL;
node->right = NULL;
treesize++;
return true;
}
else
{
if (str < node->data)
return insert(node->left, str);
else if (str > node->data)
return insert(node->right, str);
else
return false;
}
return false;
}
As you can see, I want to initialize a TREE struct inside of insert and when I finish doing this I want to link it with the left or right node of the tree.
But when I gdb this, only 1 level of tree (top level) can be constructed, the *left and *right node are NULL no matter how many strings I was trying to add to it. Why?
My TREE is:
typedef struct tree
{
string data;
tree *left;
tree *right;
} TREE;
bool TreeSet::insert(TREE *node
should be
bool TreeSet::insert(TREE *&node
Pointers can also be passed by reference, and should be if you intend to modify them directly. Otherwise you pass by copy and you now have two pointers pointing at the same location. When you use the copied one to new up some data, it now points to a new memory location, leaving your original pointer still as NULL (nullptr in C++11)
On a side note, when a tree gets constructed, you should probably initialize left and right to NULL (nullptr in C++11):
typedef struct tree
{
string data;
tree *left;
tree *right;
tree():left(NULL),right(NULL){}
} TREE;
When you pass a pointer as a parameter in the function insert like this:
bool TreeSet::insert(TREE* node, const string& str);
then the pointer is copied into the argument node. Pointing node to something else only points the local copy of the pointer. The pointer passed to the function from the call site is unchanged.
node = new TREE; // Points local variable 'node' to newly allocated data.
// When the function goes out of scope 'node' will be destroyed
// and the memory will be leaked.
To change where the original call site pointer points, take the parameter by reference (or add another level of indirection):
bool TreeSet::insert(TREE*& node, const string& str);
By taking a parameter by reference, you make sure you are accessing the call site variable and not a local copy.