C++ object pointer scope - c++

I started writing a binary tree and then came up with this example and I'm not sure what's going on. So here's the code:
#include<iostream>
using namespace std;
struct Node
{
Node *left, *right;
int key;
Node()
{
left = NULL;
right = NULL;
key = 0;
}
Node(int key)
{
left = NULL;
right = NULL;
key = key;
}
};
struct Tree
{
Node* root;
void Add(int k)
{
Node* t;
t->key = k;
root->left = t;
}
Tree(int key)
{
this->root = new Node(key);
}
};
int main()
{
Tree* tree = new Tree(5);
tree->Add(4);
cout<<tree->root->left->key;
return 0;
}
Add function Add in Tree is whats confuses me. So, there is a pointer to Node object, but new keyword is not used and it appears to me that anyway there is memory allocated in the heap because I can reach the object. Shouldn't go out of scope and be destroyed? And why I can reach that object and print out its key?

Probably that memory belongs to your program and nothing bad seems to happen because you are using so little memory. If you use more memory, some object will own that unallocated space and expect it to remain unmodified. Then this code will start giving you problems.
You are "dereferencing an uninitilized pointer". There are questions relating to this here and here, for instance. Your compiler may blow up if you do this, or it may not: the behaviour is undefined. Anything might happen, including the appearance that things are working.
Use new, like you should.

This line …
Node* t;
… is like:
Node* t = random_address;
It means that the next line …
t->key = k;
… is able to corrupt interesting memory locations.

The code is invalid. In this function
void Add(int k)
{
Node* t;
t->key = k;
root->left = t;
}
local variable t is not initialized and has indeterminate value. So the execution of the statement
t->key = k;
results in undefined behaviour.
You correctly pointed to that there must be used operator new. For example
Node* t = new Node( k );
Nevertheless even in this case the function is invalid because it has to check whether the new key is less of greater than the key of root. Depending on the condition there should be either
root->left = t;
or
root->right = t;

Related

Linked List Node object

class Node{
public:
int data;
Node* next;
Node(int data)
{
this->data = data;
this->next = NULL;
}
};
int main()
{
Node* node1 = new Node(12);
cout<< node1->data ;
return 0;
}
Can't understand that why are we creating pointer object for the node (Node* node1)?
Thanks for clearing my doubt in advance
The code in main() needs a pointer simply because that is what new returns. The code is creating the Node object in dynamic memory on the heap (and consequently leaking it, since there is no delete node1; statement before the return statement).
You could instead create the object in automatic memory on the stack, and thus not need a pointer, eg:
int main()
{
Node node1(12);
cout << node1.data;
return 0;
}
Typically, linked lists are created using dynamic memory so that new nodes can be added to the list dynamically, such as in a loop, or in response to user input, etc.

double free or corruption (out) when trying to delete Nodes in a Binary tree

I'm trying to make a recursive function to delete all nodes in my binary tree, where p->left and p->right are pointers to the next level in the tree. However it gives the following error message:
* Error in `./test.out': double free or corruption (out): 0x00007ffdf0cb3650 *
struct Node {
int key;
double data;
Node * right;
Node * left;
};
void delete_tree(Node * & p){
if (p->left){
delete_tree(p->left);
}
if (p->right){
delete_tree(p->right);
}
delete p;
};
int main(){
Node * currentNod = new Node;
currentNod->key = 5;
Node * newNode = new Node;
newNode->key = 3;
Node * newNode2 = new Node;
newNode2->key = 6;
delete_tree(currentNod);
std::cout << currentNod->key << "\n";
std::cout << newNode->key << "\n";
std::cout << currentNod->left->key << "\n";
return 0;
I've searched online and realize there can be problems when you have recursive functions with pointers, however delete_tree takes a reference, not a copy, so that problem should not apply here? I don't understand why it's not working :(
EDIT: Changed the code, the problem was that when I was initializing currentNod I first created a Node object, and then i let currentNod be a pointer to that object. When i instead initialize it like this, it works. However, it does not print what I expect. This prints:
0
3
Segmentation fault
When i would expect it to give segmentation fault right away. Does anyone have an idea what could be the problem now? Thanks :)
It would make a lot more sense to recast this as a destructor:
class Node {
public:
// some constructor, methods, ...
// destructor
~Node();
private:
int key;
double data;
Node * right;
Node * left;
};
Node::~Node(){
delete left;
delete right;
left = right = nullptr; // see note below
};
Then all you need is delete node; wherever you want to delete the tree. But if it's a subtree you also need to ensure that the parent's pointer to this node is also nulled so that you don't double-delete.
Okay, I think your problem is due to incorrect initialization of the struct Node. Since you erase all the tree recursively, you must ensure that only delete allocated dynamic memory, ie you must ensure that only delete nodes for which you have reserved dynamic memory, otherwise this will compile but will generate error at run time. That said, the correct initialization of a node before joining the tree structure should be as follows:
struct Node myNode:
myNode.right=NULL;
myNode.left=NULL;
With the above we ensure that the terminal nodes of the tree point to the NULL address, and so when we delete recursively, we will only remove nodes that do not point to this address. Then the new code for the function void delete_tree(Node * & p) Then code for function works:
void delete_tree(Node * & p){
if (p->left){
delete_tree(p->left);//Only deleted if p->left is different from NULL
}
if (p->right){
delete_tree(p->right);//Only deleted if p->right is different from NULL
}
delete p;
p=NULL; //Good Programming Practice
};

Getting wrong output with custom linked list implementation

I am learning list in C++ independently, and i have searched many websites about it. However, almost every approach to create a list is the same.
They usually create a struct as the node of a class. I want to create a class without using struct. So I created a class name ListNode which contains an int data and a pointer.
The main member functions of my class are AddNode and show.
Although, this program compiles successfully, it still does not work as I wish.
Here is the header file:
#ifndef LISTNODE_H_
#define LISTNODE_H_
#pragma once
class ListNode
{
private:
int data;
ListNode * next;
public:
ListNode();
ListNode(int value);
~ListNode();
void AddNode(ListNode* node,ListNode* headNode);
void show(ListNode* headNode);
};
#endif
Here is the implementation:
#include "ListNode.h"
#include<iostream>
ListNode::ListNode()
{
data = 0;
next = NULL;
}
ListNode::ListNode(int value)
{
data = value;
next = NULL;
}
ListNode::~ListNode()
{
}
void ListNode::AddNode(ListNode* node,ListNode* headNode) {
node->next = headNode;
headNode =node;
}
void ListNode::show(ListNode* headNode) {
ListNode * traversNode;
traversNode = headNode;
while (traversNode != NULL) {
std::cout << traversNode->data << std::endl;
traversNode = traversNode->next;
}
}
Main function:
#include"ListNode.h"
#include<iostream>
int main()
{
using std::cout;
using std::endl;
ListNode* head = new ListNode();
for (int i = 0;i < 3;i++) {
ListNode* Node = new ListNode(i);
head->AddNode(Node, head);
}
head->show(head);
return 0;
}
As far as I am concerned, the output should be
2
1
0
However, the output is a single zero. There must be something wrong in the AddNode and show function.
Could you please tell me what is wrong with these two functions?
When you call head->AddNode(node, head) you´re passing the memory directions which the pointers point, when the function arguments receive those directions, they are now pointing to the same directions, but those are another pointers, no the ones you declared in main. You could see it like this:
void ListNode::AddNode(ListNode* node,ListNode* headNode) {
/*when the arguments get their value it could be seen as something like:
node = Node(the one from main)
headNode = head(the one from main)*/
node->next = headNode;
/*Here you are modifying the new inserted node, no problem*/
headNode = node;
/*The problem is here, you´re modifying the memory direction
headNode points to, but the headNode argument of the function, no the one declared in main*/
}
So the pointer head in main() always points to the same first node you also declared in main().
In order to fix this you should change your code this way:
void ListNode::AddNode(ListNode* node,ListNode** headNode) {
/* second paramater now receives a pointer to apointer to a node */
node->next = *headNode;//the same as before but due to pointer syntaxis changes a bit
*headNode = node;//now you change the real head
}
And when you call it:
head->AddNode(Node, &head);//you use '&' before head
Now the real head, no the one in the function, will point to the last node you inserted.

Binary Tree implementation C++

Binary Tree insertion:
#include "stdafx.h"
#include <iostream>
using namespace std;
struct TreeNode {
int value;
TreeNode* left;
TreeNode* right;
};
struct TreeType {
TreeNode* root;
void insert(TreeNode* tree, int item);
void insertItem(int value) {
insert(root, value);
}
};
void TreeType::insert(TreeNode* tree, int number) {
if (tree == NULL) {
tree = new TreeNode;
tree->left = NULL;
tree->right = NULL;
tree->value = number;
cout << "DONE";
} else if (number < tree->value) {
insert(tree->left, number);
} else {
insert(tree->right, number);
}
}
int main() {
TreeType* MyTree = new TreeType;
MyTree->insertItem(8);
return 0;
}
I am currently learning Data structures in C++ and this is the code that make insertion in binary trees.
After it is compiled, everything looks fine, however when i try to execute this program, it crashed.
Can anybody tell me where I am wrong?
In your tree constructor, you need to initialize the root pointer to NULL. It's not guaranteed to be initialized as NULL.
When you compile in linux, you can use gdb to show where the source of the segfault is coming from.
Some other notes:
You should assign the value back to root after you've allocated the new node. You're not doing that because you're missing one of the fundamentals of c++. That is, it's based on c. And the thing about c is that is strictly a "by-value" function/method calling paradigm. So all parameters in a function call are by value. When you pass in the memory address for root, you're actually copying the value of the pointer. Then, you're only updating the local value. You need to assign it back to root. If you'd like to learn that concept front to back, I highly recommend watching Jerry Cain's Programming Paradigms course at Stanford.
In your main function, you should try to keep the symbol names as lower_case instead of CamelCase. This helps differentiate variables from types (types should stay CamelCase).
In your TreeType::insert method, you should call the variable tree_node instead of tree. Doing so helps reflect the correct type and avoids confusion.
Whenever possible, try you use the this->root and this->insert notation. Not only will it correctly resolve if you accidentally create a locally scoped root variable, but it's also clearer to the reader where the data or method is defined. Great coding is about communication. It may only take 100-500ms less for the reader to understand where the symbol points to; however, the tiny savings that you can accumulate in avoiding ambiguities add up into a much clearer piece of software. Your future self (and your colleagues) will thank you. See http://msmvps.com/blogs/jon_skeet/archive/2013/09/21/career-and-skills-advice.aspx
Lastly, I can't overstate enough how important learning from the source is. If you're learning c or c++ for the first time, read http://www.amazon.com/The-Programming-Language-4th-Edition/dp/0321563840 and http://www.amazon.com/Programming-Language-2nd-Brian-Kernighan/dp/0131103628. It will save you hours, upon hours, upon hours. After having learned from the source, programming is also A LOT more enjoyable because you understand a good portion of the concepts. And, the truth is, things are more fun when you have a decent level of competence in them.
Try something like this-
struct Node {
int Data;
Node* Left;
Node* Right;
};
Node* Find( Node* node, int value )
{
if( node == NULL )
return NULL;
if( node->Data == value )
return node;
if( node->Data > value )
return Find( node->Left, value );
else
return Find( node->Right, value );
};
void Insert( Node* node, int value )
{
if( node == NULL ) {
node = new Node( value );
return;
}
if( node->Data > value )
Insert( node->Left, value );
else
Insert( node->Right, value );
};
In your TreeType constructor I think you should explicitly make root point to NULL.
TreeType::TreeType() {
root = NULL;
}

Pointers and reference issue

I'm creating something similar to structure list. At the beginning of main I declare a null pointer. Then I call insert() function a couple of times, passing reference to that pointer, to add new elements.
However, something seems to be wrong. I can't display the list's element, std::cout just breaks the program, even though it compiler without a warning.
#include <iostream>
struct node {
node *p, *left, *right;
int key;
};
void insert(node *&root, const int key)
{
node newElement = {};
newElement.key = key;
node *y = NULL;
std::cout << root->key; // this line
while(root)
{
if(key == root->key) exit(EXIT_FAILURE);
y = root;
root = (key < root->key) ? root->left : root->right;
}
newElement.p = y;
if(!y) root = &newElement;
else if(key < y->key) y->left = &newElement;
else y->right = &newElement;
}
int main()
{
node *root = NULL;
insert(root, 5);
std::cout << root->key; // works perfectly if I delete cout in insert()
insert(root, 2);
std::cout << root->key; // program breaks before this line
return 0;
}
As you can see, I create new structure element in insert function and save it inside the root pointer. In the first call, while loop isn't even initiated so it works, and I'm able to display root's element in the main function.
But in the second call, while loop already works, and I get the problem I described.
There's something wrong with root->key syntax because it doesn't work even if I place this in the first call.
What's wrong, and what's the reason?
Also, I've always seen inserting new list's elements through pointers like this:
node newElement = new node();
newElement->key = 5;
root->next = newElement;
Is this code equal to:
node newElement = {};
newElement.key = 5;
root->next = &newElement;
? It would be a bit cleaner, and there wouldn't be need to delete memory.
The problem is because you are passing a pointer to a local variable out of a function. Dereferencing such pointers is undefined behavior. You should allocate newElement with new.
This code
node newElement = {};
creates a local variable newElement. Once the function is over, the scope of newElement ends, and its memory gets destroyed. However, you are passing the pointer to that destroyed memory to outside the function. All references to that memory become invalid as soon as the function exits.
This code, on the other hand
node *newElement = new node(); // Don't forget the asterisk
allocates an object on free store. Such objects remain available until you delete them explicitly. That's why you can use them after the function creating them has exited. Of course since newElement is a pointer, you need to use -> to access its members.
The key thing you need to learn here is the difference between stack allocated objects and heap allocated objects. In your insert function your node newElement = {} is stack allocated, which means that its life time is determined by the enclosing scope. In this case that means that when the function exits your object is destroyed. That's not what you want. You want the root of your tree to stored in your node *root pointer. To do that you need to allocate memory from the heap. In C++ that is normally done with the new operator. That allows you to pass the pointer from one function to another without having its life time determined by the scope that it's in. This also means you need to be careful about managing the life time of heap allocated objects.
Well you have got one problem with your Also comment. The second may be cleaner but it is wrong. You have to new memory and delete it. Otherwise you end up with pointers to objects which no longer exist. That's exactly the problem that new solves.
Another problem
void insert(node *&root, const int key)
{
node newElement = {};
newElement.key = key;
node *y = NULL;
std::cout << root->key; // this line
On the first insert root is still NULL, so this code will crash the program.
It's already been explained that you would have to allocate objects dynamically (with new), however doing so is fraught with perils (memory leaks).
There are two (simple) solutions:
Have an ownership scheme.
Use an arena to put your nodes, and keep references to them.
1 Ownership scheme
In C and C++, there are two forms of obtaining memory where to store an object: automatic storage and dynamic storage. Automatic is what you use when you declare a variable within your function, for example, however such objects only live for the duration of the function (and thus you have issues when using them afterward because the memory is probably overwritten by something else). Therefore you often must use dynamic memory allocation.
The issue with dynamic memory allocation is that you have to explicitly give it back to the system, lest it leaks. In C this is pretty difficult and requires rigor. In C++ though it's made easier by the use of smart pointers. So let's use those!
struct Node {
Node(Node* p, int k): parent(p), key(k) {}
Node* parent;
std::unique_ptr<Node> left, right;
int key;
};
// Note: I added a *constructor* to the type to initialize `parent` and `key`
// without proper initialization they would have some garbage value.
Note the different declaration of parent and left ? A parent owns its children (unique_ptr) whereas a child just refers to its parent.
void insert(std::unique_ptr<Node>& root, const int key)
{
if (root.get() == nullptr) {
root.reset(new Node{nullptr, key});
return;
}
Node* parent = root.get();
Node* y = nullptr;
while(parent)
{
if(key == parent->key) exit(EXIT_FAILURE);
y = parent;
parent = (key < parent->key) ? parent->left.get() : parent->right.get();
}
if (key < y->key) { y->left.reset(new Node{y, key}); }
else { y->right.reset(new Node{y, key}); }
}
In case you don't know what unique_ptr is, the get() it just contains an object allocated with new and the get() method returns a pointer to that object. You can also reset its content (in which case it properly disposes of the object it already contained, if any).
I would note I am not too sure about your algorithm, but hey, it's yours :)
2 Arena
If this dealing with memory got your head all mushy, that's pretty normal at first, and that's why sometimes arenas might be easier to use. The idea of using an arena is pretty general; instead of bothering with memory ownership on a piece by piece basis you use "something" to hold onto the memory and then only manipulate references (or pointers) to the pieces. You just have to keep in mind that those references/pointers are only ever alive as long as the arena is.
struct Node {
Node(): parent(nullptr), left(nullptr), right(nullptr), key(0) {}
Node* parent;
Node* left;
Node* right;
int key;
};
void insert(std::list<Node>& arena, Node *&root, const int key)
{
arena.push_back(Node{}); // add a new node
Node& newElement = arena.back(); // get a reference to it.
newElement.key = key;
Node *y = NULL;
while(root)
{
if(key == root->key) exit(EXIT_FAILURE);
y = root;
root = (key < root->key) ? root->left : root->right;
}
newElement.p = y;
if(!y) root = &newElement;
else if(key < y->key) y->left = &newElement;
else y->right = &newElement;
}
Just remember two things:
as soon as your arena dies, all your references/pointers are pointing into the ether, and bad things happen should you try to use them
if you ever only push things into the arena, it'll grow until it consumes all available memory and your program crashes; at some point you need cleanup!