"&" operator in c++ template function definitions - c++

I am writing a template BST class and notice that I need to declare the "insert" parameters like this:
void BST<TYPE>::insert(TYPE iData, int line, node *&n)
instead of:
void BST<TYPE>::insert(TYPE iData, int line, node *n)
The only difference being the "&" for passing the node pointer. The full code for the function is:
template <class TYPE>
void BST<TYPE>::insert(TYPE iData, int line, node *&n){
if(n == NULL){
n = new node(iData, line, NULL, NULL, NULL);
}
else if(iData < n->data){
insert(iData, line, n->leftChild);
}
else if(iData > n->data){
insert(iData, line, n->rightChild);
}
else if(iData == n->data){
while(n->head != NULL){ //traverse to end of list
n = n->head;
}
n->head = new node(iData, line, NULL, NULL, NULL); //add p to end of list
}
}
The tree holds duplicates, which is why there is an else if(iData == n->data).
My thought was that if we pass a pointer to a child, then eventually when NULL is found, when we create a new node, it will be at the address in memory pointed to by either the leftChild or rightChild pointer. Instead, it seems that if we don't add the & operator, the new node is created but is not "connected" to the tree.
I'm wondering if someone could explain the syntax here. Also, I apologize for any breaches of ettiquette; this is my first post on SO. This BST is for a homework assignment, but the question I've asked is not. It's just something I'm curious about.
Thanks.

When you have a function parameter int& i, any changes to i inside the function, also change the variable passed to the function. When the parameter is int i, a copy is made inside the function, and any changes to the parameter do not affect the variable passed to the function.
There's no reason the same logic cannot be applied to pointer parameters. When you have node *n, a copy of the pointer is made, and if the pointer inside the function is made to point to something else, the pointer that was passed to the function from outside is not changed. When the parameter is node *&n, if the parameter is made to point to something else inside the function, the pointer variable outside the function which was passed in, is also changed to point to the same thing.
I didn't look too much at the function, but I assume the logic requires this function to update the pointer outside the function call too.

Related

What's wrong with my avl tree add() function? [duplicate]

Printed out the address of head and &head:
head:0x603050
&head :0x7fffffffe4b8: what does this signify?
void push(node* &head,int key)// Inserts items at front of link list
{
node* linkNode=new node(); //declares new node
linkNode->data=key;
if(head==NULL) //if the link list is empty then create a new one.
{
linkNode->next=NULL;
head=linkNode; //1
}
else
{
linkNode->next=head;
head=linkNode;
}
}
Main function where all other functions are called from
link list is 8,4,2
main function
int main(int argc, char** argv)
{
node* head=NULL; //initializing head to NULL
push(head,2); //creating link list
push(head,4); //this requires &head
push(head,8); //link list is 8,4,2
selectSort(head); //this does not require &head
reverse(head); //this requires &head
return 0;
}
Why do we need to pass it by reference of reference such as in push(node* &head, int key)
Otherwise it won't work to set the given linkNode as current head:
if(head==NULL) //if the link list is empty then create a new one.
{
linkNode->next=NULL;
head=linkNode; // <- This statement changes the head variable passed from main()
}
What you have is a reference to a pointer (head) that will be 'returned' from the push() function, and set the head pointer passed from the caller correctly:
node* head=NULL;
push(head,2); // sets head to the node created for key '2'
Don't forget to delete all the node instances you have created with new node();. In a different context as you're showing, this might lead to memory leaks.
That's not a "reference of reference"; it's a reference to a pointer.
It means that, once the pointer head has been set to point to the new element, within the function, this change also affects the pointer you originally passed in to the function.
selectSort(head); //this does not require &head
It probably should do, actually, if the function performs a sort over all elements of the list.
reverse(head); //this requires &head
After this call, head now points to the new head of the list. This would not be possible if you'd passed head by value.
An alternative implementation might return the new head pointer, instead of using this "out parameter" convention.
The reason you must pass head to the push function is because your push function is expecting to modify the value of the head pointer. If you don't pass it by reference, any changes to it will only be available within the function call. For example, if it was not passed by reference, and you passed head (initialized to NULL), to the push function, a new item would be created, but your value for head would only be updated inside the function. Once you left the function, it will still be NULL (because you passed the pointer by copy).
Note that this can go away if you create a linked list class instead of treating your nodes as a linked list themselves (that is, encapsulate the nodes under a list interface - which is what the standard library does).

Exception thrown when writing an insert function for sLinkedList

I am writing a "insertBefore" function. I am checking to see if it is firstly the start of the function (This works). I then am trying to have my previous iterator point to the desired location by going through the entire list until its next is equal to the iterator passed into the function (i.e the place I want to insert in front of).
Now previous is where I want it I am trying to make its next point to the new node (the one I passed into the function) and the new node point to the old iterator after it.
Ive tried swaping the iterators instead.
template <typename T, typename InputIterator>
void SLinkedList<T, InputIterator>::insertBefore(InputIterator & t_position, T t_element)
{
//Q1
SListNode<T>* temp = t_position.get();
SListNode<T>* previous = m_head.get();
std::unique_ptr<SListNode<T>> newNode = std::make_unique<SListNode<T>>(t_element, this);
//If position is the head node
if (t_position.get() == m_head.get())
{
insertFirst(t_element);
}
else
{
while (previous != temp)
{
previous = previous->next().get();
}
if (previous == temp)
{
previous->setNext(newNode);
newNode->setNext(temp->next());
}
}
This should insert the new number I passed to insert itself into the list before the iterated position.
I suspect
previous->setNext(newNode);
is causing the issue. unique_ptr<> is moveable but not copyable. So, assuming your function setNext() did move the newNode, calling newNode->setNext() is ill formed.
And if you didn't move it (e,g - const unique_ptr<> &), then the address is being deleted once the unique_ptr goes out of scope (accessing invalid address).
You use a local variable
std::unique_ptr<SListNode<T>> newNode;
as soon as you go out of scope of void insertBefore (end of the function), this newNode will get deleted. As such, the whole list will become in an inconsistent state, and if setNext moved the value, then newNode is invalid after the first line:
previous->setNext(newNode);
newNode->setNext(temp->next());

binary search tree insertion with argument reference

I wrote the following function to practice binary search tree insertion:
void RecursiveInsert(struct Node* &root, int key) {
if (root == nullptr) {
root = new Node(key);
return;
} else if (root->key > key) {
RecursiveInsert(root->left, key);
} else {
RecursiveInsert(root->right, key);
}
}
It works, but I don't understand why should I pass in a reference to root. Does anybody know why?
You want the function to be able to change the pointer stored in the parent node of the node that you're going to insert so that it points to the new node instead of nullptr. If you don't use pass-by-reference, all the function can do is modify a local copy of the pointer, and the pointer in the actual tree won't get changed.
For example, let's say you're looking at a node, and you want to insert the new node as this node's right child.
someNode[key: 123, left: nullptr, right: nullptr]
The function is going to be called with the right pointer as an argument. You want the function to be able to change the node so it looks like this:
someNode[key: 123, left: nullptr, right: newNode]
If you don't pass-by-reference, the function can't change the right pointer of the node, since it has only been given a copy. Using the reference allows the function to actually modify the pointer stored in the node that was used as the argument.

pointer problems with function calls

I’m working on a beginner(!) exercise.
I am comfortable passing basic variables and also using &variable parameters so I can make changes to the variable that are not destroyed when returning. But am still learning pointers. I am working on the basic Mutant Bunny exercise (linked list practice).
In it I create a linked list by declaring Class Bunny. I set it up as you expect with a data section and a ‘next’ pointer for set up the linkage.
struct Bunny {
string name;
int age;
// more variables here
Bunny* next;
};
Everything works great when I call function to do things like create Bunnies using the function:
Bunny* add_node ( Bunny* in_root ){}
This sets up the node and returns it just like I want. I can also do things like call a function to modify the Bunny class like aging the bunnies.
void advanceAge ( Bunny* in_root ){}
I pass in the head and then I can modify the bunnies in the called function and it stays modified even when it goes back to main. For example I can use:
in_root->age ++;
in the called function and when I return to ‘main’ it is still changed. Basically I can use -> in any called function and it makes the change permanently. I think because the pointer is dereferenced(?) by the -> but still getting my head around it...
So far so good.
The problem comes up when I want call a function to delete the list. (Nuclear option… no more bunnies)
I can delete all the nodes in the called function… but it does not change the Bunny in ‘main’. For example… this does not permanently remove the node.
void DeathCheck(Bunny* in_root){
Bunny* prev_ptr;
prev_ptr = in_root;
if (prev_ptr == NULL){
cout << "No list to check age." << endl; return;
} else {
prev_ptr = NULL; // <- what could I code to have this stick? return;}
// rest of DeathCheck
I’m curious if there is a way to set the node to NULL in the called function and have it stick?
Since you're passing in_root by value, there's no way for it to modify the caller's variable. You could pass it by reference.
void DeathCheck(Bunny* &in_root) {
Bunny *prev_ptr = in_root;
...
in_root = nullptr;
return;
}
Currently, in DeathCheck(Bunny* in_root), there is no way that in_root can be changed, only the object it is pointing to can be changed. (See pass by reference and value with pointers). Based on this, you need to change the parameter to pass-by reference, eg by changing the signature of your function to this:
DeathCheck(Bunny* &in_root)
{
//...
}
This passes the Bunny by reference, meaning that it can now be reassigned to without a copy.

Binary Tree search returns no results (C++)

I am working on some binary tree algorithms and need a "find node with searchindex..." function. The design for treenodes is basically
class TreeNode {
int index; // some identifier
TreeNode *left;
TreeNode *right;
}
and a tree is defined by a pointer to the root-node.
My implementation for the search function is:
void Tree::searchNode(TreeNode * root, int nodeIndex, TreeNode *resultNode){
/* Recursive search */
if (root->index == nodeIndex) {
resultNode = root;
} else {
/* search children if the current node is not a leaf */
if(!root->isLeaf()) {
this->searchNode(root->left,nodeIndex,resultNode);
this->searchNode(root->right,nodeIndex,resultNode);
}
}
}
Arguments: *root is the root-node of the tree, nodeIndex is the search-index and *resultNode is the pointer to the found (or not) node in the tree.
The function does not return a reference or pointer to the found node but modifies the pointer resultNode so it points to the found node. The idea is to initialize resultNode with NULL, perform the search and modify it if a match occurs. Otherwise it remains NULL and I can easily check if there are search results or not.
Another class with a tree buildingTree as member utilizes the search-function in this way:
TreeNode *resultNodePtr = NULL;
this->buildingTree->searchNode(this->buildingTree->rootPtr,
currentNodeIndex, resultNodePtr);
// do sth. with resultNodePtr if != NULL
I create *resultNodePtr on the stack because I just need it temporarily inside the function. Is this done correctly? However: The function does not work. resultNodePtr is always NULL, even if the tree contains a node with the search-index. I debugged it very carefully step by step, it detects
(root->index == nodeIndex)
correctly but
resultNode = root;
does not work (I want resultNode to point to the same adress root points to).
Debugger says resultNode before assignment is 0x0, root node is some adress, after the assignment resultNode remains 0x0.
Do I have to overload the operator= in this case for the class TreeNode?
I have tried it:
TreeNode & TreeNode::operator=(const TreeNode & oldTreeNode){
*this = oldTreeNode;
return *this;
// ignore childs for now
}
I am not an expert but this operator= seems trivial. Does it affect the assignment of two TreeNode pointers *node1 = *node2 at all?
Maybe you can help me. Thanks for reading, appreciate your help.
If I find a solution myself I will post it here.
Regards,
Mark
Because you pass resultNode into the function as a pointer by value, its original value never changes. Think of TreeNode* as literally nothing more than a number representing a memory address; when you reassign it:
resultNode = root;
This modifies the copy that searchNode has, but not the original pointer in the code which invokes searchNode. Take this simpler example:
void Foo(int x)
{
x = 100;
}
void Bar()
{
int x = 0;
Foo(x);
// at this point, x is still 0
}
resultNode's value doesn't change from NULL for the same reason that x doesn't change from 0 when the function Bar is invoked. To fix this issue, pass the pointer in as a pointer to a pointer, or a pointer by reference:
void Tree::searchNode(TreeNode* root, int nodeIndex, TreeNode*& resultNode)
{
// same code
}
... or:
void Tree::searchNode(TreeNode* root, int nodeIndex, TreeNode** resultNodePtr)
{
// assign to *resultNodePtr instead
}
Your resultNode pointer is being passed by value, not by reference. So when the function call completes the pointer on the calling side does not receive a value.
Your algorithm looks fine :)