I am working on a Binary Search Tree (BST) program. Per the assignment's requirements, the user loads a text file to construct the tree. If the user would like, they can then create a new tree by loading a new text file. Loading a new text file should destroy the old tree.
To accommodate this require, my method to create a new tree, readNewFile(), first checks if a tree already exists. If it does, it runs the destructor on the tree. However, I then need to create a new tree, that exists outside the scope of readNewFile(), so that it can be accessed globally. Is this possible to do? If so, could you please explain how?
My reduced code:
int main() {
//BST
BST rootTree;
readNewFile(rootTree);
readNewFile(rootTree);
return 0;
}
void readNewFile(BST& tree) {
ifstream inFile;
string fileName;
// if tree was previously filled, destroy it
if (tree.rootPtr() != NULL) {
tree.~BST();
BST tree = new BST();
}
cout << "\nEnter file to load: ";
cin.ignore();
getline(cin, fileName);
cout << "Opening file " << fileName << endl;
inFile.open(fileName.c_str(), ios::in);
// Populates tree... //
}
Destructor (within BST.hpp):
BST::~BST() {
destroyTree(root);
}
void BST::destroyTree(TreeNode*& treePtr) {
if (treePtr != NULL) {
destroyTree(treePtr->leftChildPtr);
destroyTree(treePtr->rightChildPtr);
delete treePtr;
}
}
This returns a segfault, which makes sense, since the tree was destroyed. However, is there a way I can create a new tree, with the same scope as the destroyed BST rootTree?
One way to accomplish the requirement is to have a method called del() in your BST class. All it does is delete all the nodes of the tree but not the root pointer. This is the pointer that points to the root node. After calling del(), you can start creating a new tree. The pseudo code would look something like this:
// if tree was previously filled, destroy it
if (tree.rootPtr() != NULL) {
tree.del()
}
// read the input file
// Start inserting the new nodes
First off: what's wrong with the code.
As you already pointed out, if the tree is destroyed, you can't be using it again. In this case, it's double-bad: by calling the destructor on BST in readNewFile(), you're causing the destructor to be called twice -- once when you explicitly call it, and the second time when the local instance in main() goes out of scope. This is bad.
Now about how to fix it:
The easiest way would just to declare rootTree as a pointer inside main(), and then make readNewFile() take a pointer-to-pointer as an argument. readNewFile() would destroy the existing tree pointed to by the pointer, and then reset the pointer to point at the new tree it creates. Simple.
What would be a better design, though, would be to make readNewFile() a member function of BST. Then main() should construct a new BST and call that instance's readNewFile().
You never invoke the destructor directly unless you know pretty well what you're doing. And then, it's typically because it was created using placement new. Further, "T t = new T;" doesn't make sense, assigning a pointer-to-T to a T. My guess is you are reproducing a Javaism here, but the object models of these two languages are too different.
Related
My problem probably has a simple solution that is staring me in the face, but so far I have been unable to find it. I am pretty new to C languages and this is the first program I have written in C++.
I have a function create_complete_tree(int nr_child_nodes, int tree_depth) that makes a tree of depth int tree_depth in which each node (except for the last row) has int nr_child_nodes number of child nodes. create_complete_tree(2,4) makes a tree that starts out like this:
1
/ \
/ \
2 9
/ \ / \
3 6 10 13
/\ /\/\ /\
...
I am trying to make a function print(std::ostream& str) that, when called on the root node of the tree above, prints the tree contents in this format:
node_1
node_2
node_3
node_4
node_5
node_6
node_7
node_8
node_9
node_10
node_11
node_12
node_13
node_14
node_15
I will worry about adding the indents later, but right now I'm just focused on printing the nodes out in the correct order. This is what I have so far:
void node::print(std::ostream& str) {
str << this->get_name() << std::endl;
for (int i = 0; i < this->get_nr_children(); i++) {
node child = (*this->get_child(i));
child.print(str);
}
}
This function prints nodes 1-8 out, but then I get a Segmentation fault: 11 error. I know this error is a result of attempting to access memory that is somehow unavailable/off-limits, but I'm struggling to understand what that really means in my case. My create_complete_tree method looks like this:
void node::create_complete_tree(int nr_child_nodes, int tree_depth) {
if (tree_depth == 1) {
return;
} else {
while (this->get_nr_children() < nr_child_nodes) {
node* new_child = new node();
this->add_child(new_child);
(*new_child).create_complete_tree(nr_child_nodes, tree_depth - 1);
}
}
}
The child node pointers for each node are stored in a vector called child_nodes. Thanks for taking the time to read this. I'd be grateful for any responses that help me find a solution and better understand memory allocation.
Problem
This code very probably infringes the rule of 3. The following statement:
node child = (*this->get_child(i));
creates a clone of the node. If you didn't provide for the rule of 3, but implemented the destructor, the clone will use the same pointers to the same children than the original node. Unfortunately, when you then leave the print() function, the clone gets destroyed and the destructor will destroy the children. All subsequent access to these children will then access an object which no longer exist, which is UB.
Segfault is a possible symptom of UB. I cannot confirm for sure without seing the constructor, copy constructor, assignment and destructor implementation of node. But seing this code, and many similar questions here, I would be surprised that it'd be another issue ;-)
Potential solutions
The correct solution would anyhow be to implement what's missing for the trule of 3. Because you will experience similar problems in many situations if you don't.
Another solution (which is not mutually exclusive) would be to use pointer without cloning:
void node::print(std::ostream& str) {
str << this->get_name() << std::endl;
for (int i = 0; i < get_nr_children(); i++) { // this-> is not needed
node *child = this->get_child(i); // pointer assignment without cloning
child->print(str); // member invokation for a pointer
}
}
So my project is to get a file called "contacts.txt", read in the data and put the data into a node. Then, I put that node into a list. I purge duplicates and print out the resulting list. I'm trying to get the reading in the data and the printing part down first but I'm having problem printing out my list.
A line of contact looks like:
Angelina M. Pierre 306 420 1235
And each part of the line (first name, middle initial, last name, phone number) is supposed to have their own variable. I'm not really sure what I'm doing wrong and I would appreciate the help. My code is:
#include <iostream>
#include <bits/stdc++.h>
#include <stdlib.h>
#include <fstream>
using namespace std;
class Node
{
public:
string firstName;
string middleI;
string lastName;
string phoneNum;
Node *next;
};
// This function prints contents of linked list
// starting from the given node
void printList(Node* n)
{
while (n != NULL) {
cout << n->firstName->middleI->lastName->phoneNum << endl;
n = n->next;
}
}
//This function reads the data from a file called "contacts"
//And streams each line into a new node.
void readData(Node* &p)
{
Node *newNode = new Node; /* Initializing the node*/
ifstream fin("C:\\Users\\owner\\Documents\\contacts.txt");
p = newNode;
while(!EOF){
//fin >> firstName >> middleI >> lastName >> phoneNum;
//while(getline(fin,newNode->contacts)){
newNode->firstName;
newNode->middleI;
newNode->lastName;
newNode->phoneNum;
newNode->next = new Node;
newNode = newNode->next;
}
}
// Driver code
int main()
{
Node *head;
readData(head);
printList(head);
return 0;
}
There are a couple things that I think are majorly detracting from your program's performance. In your printList function, you have the line cout << n->firstName->middleI->lastName->phoneNum << endl;, and I assume here you intend to print all of the information for a user. What is happening here, however, is that the program takes the pointer n, attempts to find the firstName property of the object being pointed to, then takes this property and attempts to find the middleI property of that property, then the lastName property of that property, etc. These fields of course do not exist, so your program will likely crash. Rather, I think using something like cout << n->firstName << " " << n->middleI << " " << n->lastName << " " << n->phoneNum << endl; would work better.
Also, in your readData function, your while loop will continue to update the singular node p instead of creating new nodes, so (assuming that your input file is properly formatted and all that jazz) your head node, which is what is passed into this function when it is called by main(), will only be equal to the last contact in your file and your list will have a length of 1.
Incidentally, I see that you only have a Node class. If you are wanting to work with lists, you probably should create a second class (i.e. LinkedList) that takes one more level of abstraction. Your Node class, then, will handle setting/reporting its data and answering which node follows it, and your LinkedList class will handle keeping track of the list (by remembering where the head is), adding to/deleting from the list, and finding specific nodes in the list.
Some other considerations:
Variables held by a class should almost always be private instead of public. The reason for encapsulating the information in the first place, aside from organizing it, is to make sure that other parts of the program that have no business altering this portion of your code cannot touch it, and you lose this safeguard when you make everything public.
The functions that you are using to create/add nodes, print lists, etc., should all be methods (i.e. functions of a particular class). Say I have some class, Foo, which has a function that acts upon it named bar. To implement it, I could write something like:
class Foo {
private:
//Foo's variables
public:
void bar() {
//bar's implementation
}
}
You will be able to use the method bar() elsewhere because it is labeled public, and bar() will be responsible for handling any necessary manipulation of Foo's information.
It is considered bad practice to use using namespace std; because it can sometimes lead to ambiguous function calls and adding std:: is more explicit. See here for more information.
Using the keyword NULL is very C-style, whereas nullptr is considered more proper (and even safer) in C++. If you are curious, this seems to give a pretty in-depth explanation of this change.
Using while(!fin.eof()) is also considered wrong because !fin.eof() will only return true after you have finished reading the input file. Thus, you will attempt to read past the end of the file and this is plain dangerous. See here for more information.
A little lengthy, but I hope this clarifies things for you a bit! Feel free to comment if you have any questions.
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.
I have this tree structure:
public:
node(string& const n);
virtual ~node();
string get_name() const;
void set_name(string& new_name);
int get_nr_children() const;
node get_child(int i) const;
void add_child(node child);
private:
string& name;
vector<node> children;
};
and my main.cpp looks like this:
int main() {
string s = "root";
node r(s);
string s2 = "left child";
node ls(s2);
string s3 = "right child";
node rs(s3);
r.add_child(ls);
r.add_child(rs);
r.~node();
}
(I know that ~node() is run on all objects anyway at the end of the main funciton, but I want to make sure it is executed on the root r first)
All methods are working fine so far, except for the destructor. This is my first destructor, and I came up with the following recursive try, but don't know why it won't work.
node::~node() {
cout << "Enter ~node of " << this->get_name() << endl;
while (this->get_nr_children() != 0) {
this->get_child(0).~node();
this->children.pop_back();
}
delete this;
cout << "Leave ~node of " << this->get_name() << endl;
}
The result is an endless output of "Enter ~node of left child"
"(I know that ~node() is run on all objects anyway at the end of the main funciton, but I want to make sure it is executed on the root r first)"
This quote is enough to say that everything into this question is based only on misconceptions.
It doesn't even make sense to try to correct: a full rewrite is necessary.
Instead read more about destrcuctors, their invocation and what they are for.
Calling it explicitly does not suppress the implicit call of it. And double destruction is undefined behavior.
Also delete this is something very tricky requiring you must be VERY SURE of what it means.
And accessing class methods (this->...) or data after deleting them ... is only looking for troubles.
This is invalid and yields Undefined Behavior if it compiles:
vector<node> children;
At the point of this data member declaration the class node is incomplete; its size isn't yet known.
You can't use an incomplete type as item type for a standard library container (but you can use pointers).
In the destructor implementation, also
delete this;
yields Undefined Behavior, an infinite recursion calling the destructor which (in this delete expression) calls itself, and so on.
The data member declaration
string& name;
also has a strong, unpleasant odour about it, but since you're not showing the constructor implementations I can't state categorically 100% that it's wrong.
However, given the rest of the code, the chance of this being correct is infinitesimal. Just use
string name_;
Ordinarily, a tree uses nodes that are allocated on the free store. That means trafficking in pointers to nodes, and deleting nodes that are no longer in use. The code in the question doesn't follow that model: it allocates the root node on the stack, and stores node objects rather than pointers within each node. With that code, all of the compiler-generated destructors will work just fine. There is no need for any user-defined destructors.
Hi i have a general query regarding memory management in C++.
With the help of this program only i understood that new is used to allocate memory on heap and temporary variables are allocated memory on stack.And that if we are allocating memory on the heap we have to free it also manually else there will be memory leak.
But in the program i am updating an object of BST struct in a function named Insert by creating a new variable temp of type BST on heap.But then i am not sure how to free that memory.If i use the free command at the end of the function i.e free(temp) then the value stored at that memory will be lost and i will get an error if i try to access it again,and i certainly cannot use free(temp) in main as it is not a local variable to main.
Can some one tell me what should be done.
Btw i must mention that without using free(temp) also my program is working correctly but then i guess memory leak is happening which is bad.
Also i am a little confused as why my program is running without errors if i comment the destructor ~BST() but giving linker errors when i uncomment it.
#include<iostream>
#include<string>
#include<conio.h>
#include<array>
#include<stack>
#include<sstream>
#include<algorithm>
#include<vector>
#include<ctype.h>//isdigit
#include<deque>
#include<queue>
#include<map>
using namespace::std;
struct BST
{
int data;
BST *left;
BST *right;
BST(int d,struct BST* l,BST *r):data(d) , left(l) ,right(r)
{
}
BST()
{
}
//~BST();
};
void levelOrder(struct BST *root)
{
struct BST *temp=NULL;
int count =0;
deque<struct BST*> dq;
if(!root)
{
return;
}
dq.push_back(root);
count=dq.size();
while(!dq.empty())
{
temp=dq.front();
cout<<temp->data<<" ";
if(temp->left)
{
dq.push_back(temp->left);
}
if(temp->right)
{
dq.push_back(temp->right);
}
dq.pop_front();
if(--count==0)
{
cout<<endl;
count=dq.size();
}
}
}
void Insert(struct BST*root,int data)
{
//struct BST temp(data,NULL,NULL);
BST *temp = new BST(data,NULL,NULL);
temp->data =data;
temp->left= NULL;
temp->right=NULL;
if(!root)
{
return;
}
while(root)
{
if((root)->data >data)
{
(root)=(root)->left;
if(!(root)->left)
{
(root)->left=temp;
break;
}
}
else
{
(root)=(root)->right;
if(!(root)->right)
{
(root)->right=temp;
break;
}
}
}
}
int main()
{
deque<struct BST> dq1,dq2;
BST e(4,NULL,NULL);
BST f(3,NULL,NULL);
BST d(1,&f,NULL);
BST b(2,&d,&e);
BST c(8,NULL,NULL);
BST a(6,&b,&c);
levelOrder(&a);
Insert(&a,5);
cout<<a.left->right->right->data;
cout<<endl;
levelOrder(&a);
_getch();
return 0;
}
First, in C++ you should generally use new and delete (they call ctors/dtors, etc.) For arrays, use delete[]. new/delete is not compatible with malloc/free.
I guess BST is a binary search tree. So you have a tree of dynamically allocated memory.
You must free this whole tree, and which means you should do it in order too, lest you get dangling pointers.
One could significantly reduce the complexity by making sure that a BST-node always free's its children. Then when you delete the root node, it will recursively delete all the other nodes.
In my opinion, the easiest way to do this is to use a smart pointer, like shared_ptr<T>, unique_ptr<T> or auto_ptr (the last one has caveats, but I'm not gonna address them here.)
The structure would then look like:
struct BST
{
/* ctor, dtor omitted for brevity. */
std::unique_ptr<BST> left;
std::unique_ptr<BST> right;
}
Your BST-node goes out of scope, that is, you delete it, or it is allocated on the stack and the code exits the block. The destructors for left and right is called and the unique_ptr implementation makes sure to call delete on the pointer it stores.
The BST *temp created in your Insert method is the new node/subtree that you insert, you don't want to delete it until either the entire tree is destroyed or the node is deleted in some kind of Delete function that you haven't written yet.
Regarding your last point:
Running this particular program without a destructor will leak memory, but not access any invalid memory segments, that's why it runs without any errors.
When you uncomment the destructor declaration in your code, you get linker errors because you haven't defined the destructor, you have just told the compiler/linker that there should be a destructor, but there is none. Even if you just want an empty one, it would have to be ~BST() {}.
Both constructors should assign default values to the left right members, at least NULL;
They should not be assigned values outside the class. Add default parameters to the constructor. To avoid leakage you should not create the object until you need it. Alternatively have a flag, initially false, that you set to true if you've used it. And then delete on return if the flag is still false.
First of all, you should use new and delete for memory management in C++, not malloc() and free().
With that said, note that you assign another pointer, either left or right to point to the memory which is originally allocated to the variable temp. Your tree will give you access to the allocated memory, albeit via other variables than the original temp variable. This means that you can delete the memory using these variables in your BST class. Typically this is done inside the destructor.
Note that you are managing memory here, not variables. Let's look at the difference with a simple example:
int main() {
int* intPtr = new int;
int* temp = intPtr;
delete temp;
temp = NULL;
}
As you can see, this code allocates a single block of memory to store an int. This memory has two pointers to it. You can delete the memory with either pointer, just as long as you don't delete with both. This is definitely a balancing act when you learn about memory management. You must be sure that all memory is deallocated while never attempting to deallocate the same block of memory twice.