I am learning about reference pointers and memory leaks.
I have created a method that takes in a pointer to a NodeData, create a Node that contains NodeData, and a left pointer, and a right pointer, and insert it depending on its value. I am using a reference pointer to traverse my tree. I am getting a memory leak every time I declare a new Node object and go outside of the scope.
I thought that since the pointer reference is pointing to the object, it has not disappeared. My program runs fine, but when I use valgrind it comes up with an error.
I have written this method multiple ways but cannot find out a way to fix it. I have posted the most concise method I've written.
bool BinTree::insert(NodeData* s) {
Node* currentPtr = root;
bool insert = insertHelper(s, currentPtr);
return insert;
};
bool BinTree::insertHelper(NodeData* s, Node*& currentPtr) {
if (currentPtr == NULL) {
Node* newNodePtr = new Node;
newNodePtr->data = s;
newNodePtr->left = NULL;
newNodePtr->right = NULL;
currentPtr = newNodePtr;
return true;
} else if (*s == *currentPtr->data) {
return false;
} else if (*s < (*currentPtr->data)) {
return insertHelper(s, currentPtr->left);
} else {
return insertHelper(s, currentPtr->right);
}
}
I'm getting a lot of memory leaks throughout my program due to similar errors.
Or, would anyone explain the concept behind the pointer reference, or ways to learn more about it?
The method is used by this method:
void buildTree(BinTree& T, ifstream& infile) {
string s;
for (;;) {
infile >> s;
cout << s << ' ';
if (s == "$$") break; // at end of one line
if (infile.eof()) break; // no more lines of data
NodeData* ptr = new NodeData(s); // NodeData constructor takes string
// would do a setData if there were more than a string
bool success = T.insert(ptr);
if (!success)
delete ptr; // duplicate case, not inserted
}
}
You might have a bit more luck with this
bool BinTree::insert(NodeData* s) {
bool insert = insertHelper(s, root);
return insert;
};
Your code modified a local variable, not the root.
If you want to modify the root, you need a reference to the root, not to a local variable which has a copy of the root.
Related
This question already has answers here:
Warning of "Control may reach end of non-void function"
(3 answers)
Closed 1 year ago.
I was coding a BST Tree, and first i made it with integer key, everything worked fine. Then i copied my code and made some changes, i switched integer key to string key and also added one new pointer (because my goal is to create two trees, one with English words and one with their Polish translation) so i tested it just on single tree with string key first and insert function works fine like in the interger tree, but search function is returning some garbage insted of NULL or pointer to node. I dont really know what is a problem here.
I put the code of Integer tree below:
#include <iostream>
#include <fstream>
#include <string.h>
#include <string>
using namespace std;
typedef struct BST
{
int key;
BST* right;
BST* left;
}BST_node;
BST_node* CreateNewNode(int data) // function that returns new node of my tree
{
BST_node* NewNode = new BST_node;
NewNode->key = data;
NewNode->right = NULL;
NewNode->left = NULL;
return NewNode;
}
BST_node* bstSearch(BST_node* root, int data) // search function
{
if (root == NULL)
return NULL;
else if (root->key == data)
return root;
else if (root->key < data)
bstSearch(root->right, data);
else
bstSearch(root->left, data);
}
void bstInsert(BST_node*& root, int data) // insert function
{
if (root == NULL)
root = CreateNewNode(data);
if (data < root->key)
bstInsert(root->left, data);
else if (data > root->key)
bstInsert(root->right, data);
}
int main()
{
ifstream in1("InTest1.txt"); // InTest1.txt:1 2 4 3 5 52 2 4
BST_node* root = NULL;
int suppVar;
while (!in1.eof())
{
in1 >> suppVar;
bstInsert(rootEng, suppVar);
}
BST_node* tmp = bstSearch(rootEng, 2);
if (tmp == NULL)
cout << "There is no element with given key";
else
cout << "key = " << tmp->key;
}
OUT: key = 2
And also i put the code of string key version of my tree below:
#include <iostream>
#include <fstream>
#include <string.h>
#include <string>
using namespace std;
typedef struct BST_str
{
string key;
BST_str* right;
BST_str* left;
BST_str* engWordPtr; // pointer to node in translation tree (not used yet)
}BST_strNode;
BST_strNode* CreateNewStrNode(string data) // function that returns new node of my tree
{
BST_strNode* NewNode = new BST_strNode;
NewNode->key = data;
NewNode->right = NULL;
NewNode->left = NULL;
NewNode->engWordPtr = NULL;
return NewNode;
}
BST_strNode* bstStrSearch(BST_strNode* root, string data) // search function
{
if (root == NULL)
return NULL;
else if (strcmp(root->key.data(), data.data()) == 0)
return root;
else if (strcmp(root->key.data(), data.data()) < 0)
bstStrSearch(root->right, data);
else if (strcmp(root->key.data(), data.data()) > 0)
bstStrSearch(root->left, data);
}
void bstStrInsert(BST_strNode*& root, string data) // insert function
{
if (root == NULL)
root = CreateNewStrNode(data);
else if (strcmp(root->key.data(), data.data()) > 0)
bstStrInsert(root->left, data);
else if (strcmp(root->key.data(), data.data()) < 0)
bstStrInsert(root->right, data);
}
int main()
{
ifstream in1("InTest2.txt"); // InTest2.txt:O G X E OH D F I OA H OB OX
BST_strNode* rootEng = NULL;
string suppVar;
while (!in1.eof())
{
in1 >> suppVar;
bstStrInsert(rootEng, suppVar);
}
BST_strNode* tmp = bstStrSearch(rootEng, "OXcasdf");
if (tmp == NULL)
cout << "There is no element with given key";
else
cout << "key = " << tmp->key;
}
OUT: key =
And program crashes, it doesnt matter if i want to search for string that is already there or not, always the same result, probably its returning some garbage instead of node or NULL but i don't really know why it's working on integer tree, but on string tree doesn't. It also generates 3 warnings:
Warning C26495 Variable 'BST_str::engWordPtr' is uninitialized. Always initialize a member variable (type.6).
Warning C26495 Variable 'BST_str::left' is uninitialized. Always initialize a member variable (type.6).
Warning C26495 Variable 'BST_str::right' is uninitialized. Always initialize a member variable (type.6).
And also an exception while debugging:
Exception thrown: read access violation. this was 0x45.
Thanks for the help in advance.
The recursive function bstSearch is incorrect because it does not return a node in each its path of execution
BST_node* bstSearch(BST_node* root, int data) // search function
{
if (root == NULL)
return NULL;
else if (root->key == data)
return root;
else if (root->key < data)
bstSearch(root->right, data);
else
bstSearch(root->left, data);
}
The last if else statements should look like
else if (root->key < data)
return bstSearch(root->right, data);
else
return bstSearch(root->left, data);
Also for the function designed for strings there is no need to use the C function strcmp. The function could be defined the following way
BST_strNode* bstStrSearch( BST_strNode* root, const string &data) // search function
{
if (root == NULL)
return NULL;
else if ( root->key == data )
return root;
else if ( root->key < data )
return bstStrSearch(root->right, data);
else
return bstStrSearch(root->left, data);
}
Pay attention to that the condition of the while loop
while (!in1.eof())
{
in1 >> suppVar;
bstStrInsert(rootEng, suppVar);
}
is incorrect. The eof state can occur after this statement
in1 >> suppVar;
Instead you should write
while ( in1 >> suppVar)
{
bstStrInsert(rootEng, suppVar);
}
Note when compiled, the compiler should print a warning along the lines of:
warning: control may reach end of non-void function
in reference to bstStrInsert. Indeed, looking at the function definition, the two recursive branches don't return a value.
To prevent the warning (and this sort of error in general), you can use a local variable to hold the result, and have a single return.
Additionally, the functions should be rewritten as member function of the BST node class. You can also use templates (and template specializations) rather than creating separate, unrelated BST classes for each key type. With scohe001's protip, the template functions will work with any key type that implements standard comparison operators (so you don't have to write a specialization for std::string).
template<typename K> BST_Node<K>* BST_Node<K>::search(BST_Node<K>* node, const K& data) {
BST_Node<K>* result = NULL;
if (node) {
if (node->key == data)
result = node;
else if (node->key < data)
result = search(node->right, data);
else // if (node->key > data)
result = search(node->left, data);
}
return result;
}
Since the last branch covers all remaining cases, the if (node->key > data) test is unnecessary.
The above BST_Node<K>::search has an extra BST_Node<K>* argument that isn't strictly necessary. An alternative is to call search on each node, which means moving the pointer test to immediately before each recursive call (and operating on this, rather than the extra node argument).
template<typename K> BST_Node<K>* BST_Node<K>::search(const K& data) {
BST_Node<K>* result = NULL;
if (key == data)
result = this;
else if (key < data) {
if (right) {
result = right->search(data);
}
} else if (left) // (key > data)
result = left->search(data);
return result;
}
In general, an interactive debugger is your most powerful tool for troubleshooting crashes and unexpected behavior. Find and learn to use whatever debugger your development suite provides.
Additional
As noted in C++ references, passing string::data to C string functions can result in undefined behavior, as it's not guaranteed to be null terminated. string::c_str should be used for that purpose, but (in general) C string functions should only be used when interacting with C code (such as libraries).
When printing a message, be sure to include a newline. This can be done with a newline in the string, but better is to output std::endl, which will also flush the output buffer (if output is buffered, which it probably is).
Importing all of std into the current namespace is fine for one-offs, sample and practice code, but shouldn't be done in production. Importing specific symbols (such as std::cin, std::cout and std::endl) is fine and unlikely to cause collisions.
I have a question on how to resolve a memory leak for an assignment in my c++ class. In this assignment, I am supposed to implement a BST. I have done so and functionality wise, everything works as I expected. However, I am have a memory leak in my program. The only problem is, when I try to debug my memory leak with Valgrind, I get pointed to a line in my compiled code that I did not write. In the function below, and I did not write this function, this was given to me by my professor, Valgrind points to the line NodeData* ptr = new NodeData(s); in the function below:
void buildTree(BinTree& T, ifstream& infile) {
string s;
for (;;) {
infile >> s;
cout << s << ' ';
if (s == "$$") break; // at end of one line
if (infile.eof()) break; // no more lines of data
NodeData* ptr = new NodeData(s); // NodeData constructor takes string
// would do a setData if there were more than a string
bool success = T.insert(ptr);
if (!success)
delete ptr; // duplicate case, not inserted
}
}
In the driver file for my BST, a file created by my professor, he is allocating NodeData pointers. The NodeData class was also given to me by the professor, but the problem (I think) is that the destructor for the NodeData class is empty. Here is the .cpp file for the NodeData class:
#include "nodedata.h"
//------------------- constructors/destructor -------------------------------
NodeData::NodeData() { data = ""; } // default
NodeData::~NodeData() { } // needed so strings are deleted properly
NodeData::NodeData(const NodeData& nd) { data = nd.data; } // copy
NodeData::NodeData(const string& s) { data = s; } // cast string to NodeData
//------------------------- operator= ----------------------------------------
NodeData& NodeData::operator=(const NodeData& rhs) {
if (this != &rhs) {
data = rhs.data;
}
return *this;
}
//------------------------- operator==,!= ------------------------------------
bool NodeData::operator==(const NodeData& rhs) const {
return data == rhs.data;
}
bool NodeData::operator!=(const NodeData& rhs) const {
return data != rhs.data;
}
//------------------------ operator<,>,<=,>= ---------------------------------
bool NodeData::operator<(const NodeData& rhs) const {
return data < rhs.data;
}
bool NodeData::operator>(const NodeData& rhs) const {
return data > rhs.data;
}
bool NodeData::operator<=(const NodeData& rhs) const {
return data <= rhs.data;
}
bool NodeData::operator>=(const NodeData& rhs) const {
return data >= rhs.data;
}
//------------------------------ setData -------------------------------------
// returns true if the data is set, false when bad data, i.e., is eof
bool NodeData::setData(istream& infile) {
getline(infile, data);
return !infile.eof(); // eof function is true when eof char is read
}
//-------------------------- operator<< --------------------------------------
ostream& operator<<(ostream& output, const NodeData& nd) {
output << nd.data;
return output;
}
Its pretty short, but you can see that the destructor is empty. I dont know if I am allowed to edit the destructor of NodeData to resolve this memory leak and I am not sure how/where else in my code I can delete these pointers (as I cannot edit the driver file itself either). This isnt something I have ever dealt with before and what I need help with. Can someone help me out with this and give me suggestions on ways that I can investigate on how to resolve this memory leak?
Is it possible to resolve the memory leak caused by not deleting the pointers created by: NodeData* ptr = new NodeData(s); in the function the pointers are being passed to (the insert function. I can provide this code if needed).
If it is not possible to resolve the memory leak inside the insert function, is there another place I can feasibly fix it? I think I might be able to edit the NodeData class/make a destructor, but am not totally sure on how to implement such a destructor function in the NodeData class.
And as it is probably helpful, here is the insert function of the BinTree class that is referenced by the buildTree function:
bool BinTree::insert(NodeData* nodeData){
if(root != nullptr){ //if the root of the tree is not NULL, meaning that the BST object exists, we call our insertHelp function.
insertHelp(this->root, nodeData);
return true;
}
else{ //If there is no root node created in our tree, we create a root node.
root = new Node;
root->left = nullptr;
root->right = nullptr;
root->data = nodeData;
return true;
}
return true;
}
// ---------------------------------insertHelp--------------------------------------------------
// Description: Private function that recursively calls itself in order to find the correct location in a BinTree object to add a new node.
// ---------------------------------------------------------------------------------------------------
bool BinTree::insertHelp(Node *nodePointer, NodeData* nodeData){
if(*nodeData < *nodePointer->data){ //Here we are deciding if we need to traverse left or not.
if(nodePointer->left != nullptr){ //If there is a node to the left, we call insertHelper again but on this node to the left.
insertHelp(nodePointer->left, nodeData);
}
else{ //if there is no node the left, and we need to go left, we create a new left node.
nodePointer->left = new Node;
nodePointer->left->left = nullptr;
nodePointer->left->right = nullptr;
nodePointer->left->data = nodeData;
return true;
}
}
else if(*nodeData > *nodePointer->data){//Here we are deciding if we need to traverse right or not.
if(nodePointer->right != nullptr){ //if there is no node the right, and we need to go left, we create a new left node.
insertHelp(nodePointer->right, nodeData);
}
else{ //if there is no node the right, and we need to go right, we create a new right node.
nodePointer->right = new Node;
nodePointer->right->left = nullptr;
nodePointer->right->right = nullptr;
nodePointer->right->data = nodeData;
return true;
}
}
else{ //catch all case. If something goes wrong, return false.
return false;
}
return true;
}
Here is the current destructor for BinTree:
BinTree::~BinTree(){
makeEmpty();
}
void BinTree::makeEmpty(){
//You make a tree empty by deleting all notes in a post-order traversal.
postOrderDeleteNode(this->root); //I will call my private postOrderDelete helper function. I broke things up this way to make the code cleaner.
}
// ---------------------------------postOrderDeleteNode--------------------------------------------------
// Description: This is the private function that performs a post-order traversal of the nodes in a BinTree
// and deletes each node by deallocating the memory assigned when each node is created.
// ---------------------------------------------------------------------------------------------------
void BinTree::postOrderDeleteNode(const Node *rootNode){
if(rootNode == nullptr){ //Base case, we have an empty tree at this node
return;
}
else{
postOrderDeleteNode(rootNode->left); //First we delete the left side of the tree
postOrderDeleteNode(rootNode->right); //Then we delete the right side of the tree.
delete rootNode; //We finally delete the root of the entire BST.
this->root = nullptr;
}
}
And here is the .h file for BinTree:
// ------------------------------------------------bintree.h-------------------------------------------------------
//
// Programmer Name: Aviv Weinstein
// Course Section Number: CSS 502 A
// Creation Date: 1/17/21
// Date of Last Modification: 1/27/21
// Instructor Name: Professor Dong Si
// --------------------------------------------------------------------------------------------------------------------
// Purpose -
// --------------------------------------------------------------------------------------------------------------------
// Notes on specifications, special algorithms, and assumptions:
// --------------------------------------------------------------------------------------------------------------------
#ifndef Bintree_H
#define Bintree_H
#include "nodedata.h"
//We have inlcuded iostream in nodedata.h
using namespace std;
class BinTree{
friend ostream& operator<<(ostream& out, const BinTree& T); //Used for output printing of BinTree objects. To display the tree using inorder traversal.
public:
BinTree(); // constructor
BinTree(const BinTree &); // copy constructor
~BinTree(); // destructor, calls makeEmpty
bool isEmpty() const; // true if tree is empty, otherwise false
void makeEmpty(); // make the tree empty so isEmpty returns true
BinTree& operator=(const BinTree &); //The assignment operator (=) to assign one tree to another
bool operator==(const BinTree &) const; //Boolean comparison operator for equal
bool operator!=(const BinTree &) const; //Boolean comparison operator for NOT equal
bool insert(NodeData*); //inserts a new node, with NodeData, into the BinTree object.
bool retrieve(const NodeData &, NodeData* &) const; //Looks for a specific node in the BinTree. Returns true if the node exists.
void displaySideways() const; // provided below, displays the tree sideways
int getHeight(const NodeData &) const; //function to find the height of a given value in the tree.
void bstreeToArray(NodeData* []); //function to fill an array of Nodedata* by using an inorder traversal of the tree
void arrayToBSTree(NodeData* []); //function to fill an array of Nodedata* by using an inorder traversal of the tree
private:
struct Node {
NodeData* data; // pointer to data object
Node* left; // left subtree pointer
Node* right; // right subtree pointer
};
Node* root; // root of the tree
//Utility functions
void sideways(Node*, int) const; //provided below, helper for displaySideways()
bool insertHelp(Node *nodePointer, NodeData* nodeData); //Helper function for the insert function.
void postOrderDeleteNode(const Node *node); // Helper function. Deletes all nodes in a BinTree object
void inorderHelper(Node *startNode) const; //Helper function for printing out all nodes in a BST using in-order traversal
void inorderHelperArray(NodeData* a[], Node *startNode) const; //Helper function for the operator == and operator!= functions.
//Used to compare BSTs to each other using in-order traversal.
Node* retrieveHelper(Node *root, const NodeData &nodeData) const; //A hlper function for retriving a node in a BinTree
int getHeightUtil(Node *node)const; //Helper for performing a recursive calculation of the height of a node in a BinTree.
void preorderTraversal(Node* node); //Performs a pre-order traversal of a BinTree object. Called by the operator= function.
void convert(NodeData* a[], int start, int end, Node *root); //Performs a utility function in the arraytoBSTree function.
//Selects the correct array indexes to be added next into an array.
};
#endif
Every time you call new, there must be corresponding delete for it. In your example, you call new, then store node in BinTree. I think you do not delete this node from BinTree once tree gets deallocated. Storing std::shared_ptr instead of raw pointer will resolve this problem.
It is always better to use automatic memory management
void buildTree(BinTree& T, ifstream& infile) {
string s;
for (;;) {
infile >> s;
cout << s << ' ';
if (s == "$$") break; // at end of one line
if (infile.eof()) break; // no more lines of data
// ptr will be removed automatically when it goes out of scope
std::shared_ptr<NodeData> ptr = std::make_shared<NodeData>(s);
// would do a setData if there were more than a string
// tree insertion would increase ptr scope to the scope of a T instance,
// so if T is deallocated, all nodes will be deallocated automatically
T.insert(ptr);
}
}
If, for some reason, you do not want/can't use shared_ptr, then change nothing in buildTree and make BinTree::insert convert raw pointer to std::shared_ptr inside:
bool BinTree::insert(NodeData* nodeData){
if(root != nullptr){ //if the root of the tree is not NULL, meaning that the BST object exists, we call our insertHelp function.
insertHelp(this->root, nodeData);
return true;
}
else{ //If there is no root node created in our tree, we create a root node.
root = new Node;
root->left = nullptr;
root->right = nullptr;
// update ---vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
root->data = std::shared_ptr<NodeData>(nodeData);
return true;
}
return true;
}
bool BinTree::insertHelp(Node *nodePointer, NodeData* nodeData){
if(*nodeData < *nodePointer->data){ //Here we are deciding if we need to traverse left or not.
if(nodePointer->left != nullptr){ //If there is a node to the left, we call insertHelper again but on this node to the left.
insertHelp(nodePointer->left, nodeData);
}
else{ //if there is no node the left, and we need to go left, we create a new left node.
nodePointer->left = new Node;
if(!nodePointer->left) return false;
nodePointer->left->left = nullptr;
nodePointer->left->right = nullptr;
nodePointer->left->data = std::shared_ptr<NodeData>(nodeData);
return true;
}
}
else if(*nodeData > *nodePointer->data){//Here we are deciding if we need to traverse right or not.
if(nodePointer->right != nullptr){ //if there is no node the right, and we need to go left, we create a new left node.
insertHelp(nodePointer->right, nodeData);
}
else{ //if there is no node the right, and we need to go right, we create a new right node.
nodePointer->right = new Node;
if(!nodePointer->right) return false;
nodePointer->right->left = nullptr;
nodePointer->right->right = nullptr;
nodePointer->right->data = std::shared_ptr<NodeData>(nodeData);
return true;
}
}
else{ //catch all case. If something goes wrong, return false.
return false;
}
return true;
}
This comes down to a bug in your insertHelp and insert functions as well as the fact that you do not delete the data object of your node.
If you take a close look at insert, you'll notice that it always returns true so even if you attempt to insert a duplicate, buildTree never deletes that object.
// we need to take the return values of the recursive calls in this function
bool BinTree::insertHelp(Node* nodePointer, NodeData* nodeData) {
if (*nodeData < *nodePointer->data) { //Here we are deciding if we need to traverse left or not.
if (nodePointer->left != nullptr) { //If there is a node to the left, we call insertHelper again but on this node to the left.
return insertHelp(nodePointer->left, nodeData);
}
else { //if there is no node the left, and we need to go left, we create a new left node.
nodePointer->left = new Node;
nodePointer->left->left = nullptr;
nodePointer->left->right = nullptr;
nodePointer->left->data = nodeData;
return true;
}
}
else if (*nodeData > *nodePointer->data) {//Here we are deciding if we need to traverse right or not.
if (nodePointer->right != nullptr) { //if there is no node the right, and we need to go left, we create a new left node.
return insertHelp(nodePointer->right, nodeData);
}
else { //if there is no node the right, and we need to go right, we create a new right node.
nodePointer->right = new Node;
nodePointer->right->left = nullptr;
nodePointer->right->right = nullptr;
nodePointer->right->data = nodeData;
return true;
}
}
else { //catch all case. If something goes wrong, return false.
return false;
}
return true;
}
bool BinTree::insert(NodeData* nodeData) {
if (root != nullptr) { //if the root of the tree is not NULL, meaning that the BST object exists, we call our insertHelp function.
return insertHelp(this->root, nodeData); // <<<<<<<<<<<<<<< use the return value of the helper function here!!!
}
else { //If there is no root node created in our tree, we create a root node.
root = new Node;
root->left = nullptr;
root->right = nullptr;
root->data = nodeData;
return true;
}
return true;
}
void BinTree::makeEmpty() {
//You make a tree empty by deleting all notes in a post-order traversal.
postOrderDeleteNode(this->root); //I will call my private postOrderDelete helper function. I broke things up this way to make the code cleaner.
this->root = nullptr; // needs to be done just once
}
// ---------------------------------postOrderDeleteNode--------------------------------------------------
// Description: This is the private function that performs a post-order traversal of the nodes in a BinTree
// and deletes each node by deallocating the memory assigned when each node is created.
// ---------------------------------------------------------------------------------------------------
void BinTree::postOrderDeleteNode(const Node* rootNode) {
if (rootNode == nullptr) { //Base case, we have an empty tree at this node
return;
}
else {
postOrderDeleteNode(rootNode->left); //First we delete the left side of the tree
postOrderDeleteNode(rootNode->right); //Then we delete the right side of the tree.
delete rootNode->data; // <<<<<<<<<<<<<<<<<<<<<<<< delete the data too!!!!
delete rootNode; //We finally delete the root of the entire BST.
// set root to null it in makeEmpty() instead
}
}
I am having a few simple troubles regarding my code. What I am trying to do is build my own "list" code for 2 reasons. First of all, I want to get a deeper understanding of how linked lists work in practice. And secondly, because I am working on a project that requires a list of references, not just copied objects like the standard "list" library offers. I am also attempting to keep my code similiar to how C# works, while keeping the simplicity of python's naming scheme.
The following code is my issue:
// & - Get the address of object
// * - Get the contents of pointer
// int x = 25;
// int* p = &x;
// "p" now contains the pointer address
// "*p" contains the same info as x
// The following are all equal
// x = x + 5;
// x = *p + 5;
// *p = *p + 5;
template <class T> class Link;
template <class T> class Link {
public:
T result;
bool containsData = false;
Link<T> *x;
};
template <class T> class list {
public:
Link<T> start;
int count = 0;
Link<T> *blank(T object) {
Link<T> temp;
return &temp;
}
void append(T object) {
print("Starting");
Link<T> *temp = &start;
while (true) {
print("1");
if (temp->containsData == false) {
break;
}
print("2");
Link<T> *next = temp->x;
temp = next;
}
print("Doing");
temp->containsData = true;
temp->x = blank(object);
print("Done");
count++;
}
void pop(int index);
void clear();
void reverse();
T get(int index);
};
What is happening is that the first iteration works well. However, the second appended item in the list seems to halt at "1", seeming to be completely incapable of moving past "temp->containsData". I've tried putting it in a try-catch block, but it would seem that not even try-catch picks it up for some reason (Which surprised me quite a bit!). It just exits in the middle of trying to find out if the next item in the list is just another link to search through, or whether it is the last one in the list (Hence, containsData set to false).
I have checked, and yes it is the "temp->containsData" part that just crashes out. It seems trying to access "containsData" randomly crashes it. I am aware the code isn't too pretty, that's mainly from my debugging.
If anyone could throw me some pointers (Pun not-intended) as to how to continue, I would be incredibly grateful. Again, I do not want to just use the inbuilt "list" library and call it a day. I want to legit work through this coding trouble.
Thanking you in advance
Andrey :)
Link<T> *blank(T object) {
Link<T> temp;
return &temp;
}
is bad because it is returning a pointer to non-static local object, which will vanish on returning from this function.
Try this instead:
Link<T> *blank(T object) {
return new Link<T>();
}
Just like magic, the moment I post this, THEN stackoverflow decides to point me to a useful post. I've been looking online for hours!
Thanks for the (Surprisingly) quick responses! I managed to find a better way of doing it. Turns out that I was goind about it a bit of an unnecessarily complicated way. The following code seems to work:
template <class T> class Link {
public:
Link<T> *next;
T *item;
};
template <class T> class list {
public:
Link<T> *start = new Link<T>();
int count = 0;
void append(T *object) {
Link<T> *currentValue = start;
while (currentValue->next != nullptr) {
currentValue = currentValue->next;
}
currentValue->next = new Link<T>();
currentValue->item = object;
print(object);
count++;
}
void pop(int index) {
if (index >= count) { throw "index larger than size!"; }
if (index == 0) {
//Deleting the first item in the list
if (count == 1) {
start = new Link<T>();
} else {
start = start->next;
}
} else if (index == count - 1){
//Deleting the last item in the list
Link<T> *currentValue = start;
while (currentValue->next->next != nullptr) {
currentValue = currentValue->next;
}
currentValue->next = new Link<T>();
} else if (index == count - 1) {
//Deleting somewhere in the middle of the list
}
count--;
}
void clear() { start = new Link<T>(); count = 0; }
T get(int index) {
if (index >= count) { throw "index larger than size!"; }
int looper = 0;
Link<T> *currentValue = start;
while (looper != index) {
currentValue = currentValue->next;
looper++;
}
T *temp = currentValue->item;
return *temp;
}
};
Much smaller, works fine, links everything properly.
HOWEVER, thankyou to all the people pointing out that a pointer will disappear once the reference to it disappears. I completely forgot about that.
Thankyou so much for all your help, and I shall (hopefully) be able to finish this code pretty quickly :)
I am really stuck, I'm getting an error at "CTree.add(num);" saying 'CTree' is undeclared, which doesn't make sense because I initialized it in tree.h?
The program is supposed to prompt the user, the user enters a command (i.e. "add 3", only 0-9 integers) and then I want it to insert that number into the tree.
//File: tree.h
class CTree
{
private:
CTree* m_pLeft;
CTree* m_pRight;
CTree* m_pRoot;
int m_nData;
public:
CTree();
bool isEmpty() const { return m_pRoot; }
bool search(int);
void print_inorder();
void inorder(CTree*);
void Add(int);
void remove(int);
void height();
};
//File: CTree.cpp
#include <iostream>
#include <cstdlib>
using namespace std;
CTree::CTree()
{
m_pRoot=NULL;
}
bool CTree::search(int x)
{
if(x==m_nData) return true;
if(x < m_nData){ //go left
if(m_pLeft != NULL) //if possible
return m_pLeft->search(x);
}
else //go right
if(m_pRight != NULL) //ifpossible
return m_pRight->search(x);
return false;
}
void CTree::Add(int x)
{
CTree* t = new CTree;
CTree* parent;
t->m_nData = x;
t->m_pLeft = NULL;
t->m_pRight = NULL;
parent = NULL;
if(isEmpty()) m_pRoot = t;
else
{
//insert leaf nodes
CTree* leaf;
leaf = m_pRoot;
// find parent
while(leaf)
{
parent = leaf;
if(t->m_nData > leaf->m_nData)
leaf = leaf->m_pRight;
else
leaf = leaf->m_pLeft;
}
if(t->m_nData < parent->m_nData)
parent->m_pLeft = t;
else
parent->m_pRight = t;
}
}
void CTree::remove(int x)
{
bool found = false;
if(isEmpty())
{
cout<< "Tree is empty!" <<endl;
return;
}
CTree* current;
CTree* parent;
current = m_pRoot;
while(current != NULL)
{
if(current->m_nData == x)
{
found = true;
break;
}
else
{
parent = current;
if(x > current->m_nData) current = current->m_pRight;
else current = current->m_pLeft;
}
}
if(!found)
{
cout<< "Not found!" <<endl;
return;
}
// Node with single child
if((current->m_pLeft == NULL && current->m_pRight != NULL)|| (current->m_pLeft != NULL&& current->m_pRight != NULL))
{
if(current->m_pLeft == NULL && current->m_pRight != NULL)
{
if(parent->m_pLeft == current)
{
parent->m_pLeft = current->m_pRight;
delete current;
}
else
{
parent->m_pRight = current->m_pRight;
delete current;
}
}
else // left child present, no right child
{
if(parent->m_pLeft == current)
{
parent->m_pLeft = current->m_pLeft;
delete current;
}
else
{
parent->m_pRight = current->m_pLeft;
delete current;
}
}
return;
}
//We're looking at a leaf node
if( current->m_pLeft == NULL && current->m_pRight == NULL)
{
if(parent->m_pLeft == current) parent->m_pLeft = NULL;
else parent->m_pRight = NULL;
delete current;
//Node with 2 children
// replace node with smallest value in right subtree
if (current->m_pLeft != NULL && current->m_pRight != NULL)
{
CTree* check;
check = current->m_pRight;
if((check->m_pLeft == NULL) && (check->m_pRight == NULL))
{
current = check;
delete check;
current->m_pRight = NULL;
}
else // right child has children
{
//if the node's right child has a left child
// Move all the way down left to locate smallest element
if((current->m_pRight)->m_pLeft != NULL)
{
CTree* lcurrent;
CTree* lcurrent_parent;
lcurrent_parent = current->m_pRight;
lcurrent = (current->m_pRight)->m_pLeft;
while(lcurrent->m_pLeft != NULL)
{
lcurrent_parent = lcurrent;
lcurrent = lcurrent->m_pLeft;
}
current->m_nData = lcurrent->m_nData;
delete lcurrent;
lcurrent_parent->m_pLeft = NULL;
}
else
{
CTree* tmp;
tmp = current->m_pRight;
current->m_nData = tmp->m_nData;
current->m_pRight = tmp->m_pRight;
delete tmp;
}
}
return;
}
}
}
void CTree::print_inorder()
{
inorder(m_pRoot);
}
void CTree::inorder(CTree* x)
{
if(x != NULL)
{
if(x->m_pLeft) inorder(x->m_pLeft);
cout<<" "<<x->m_nData<<" ";
if(x->m_pRight) inorder(x->m_pRight);
}
else return;
}
//File: main.cpp
#include <iostream>
#include <cstdlib>
#include <sstream>
#include <locale>
#include <string>
#define PROMPT "bst> "
using namespace std;
int getNumber(string s)
{
int num;
for(int i; i<=s.length();i++)
{
if(isdigit(s[i]))
{
num= s[i]-48;
}
}
return num;
} // getNumber
bool process(const string& s, CTree* aTree)
{
bool mustquit=false;
int num;
istringstream iss(s);
do
{
string sub;
iss >> sub; //
if(sub=="add" || sub=="insert")
{
num=getNumber(s);
cout<<num<<endl;
aTree->Add(num);
}
else if(sub=="delete" || sub=="remove")
{
num=getNumber(s);
cout<<num<<endl;
}
else if(sub=="search" || sub=="find")
{
num=getNumber(s);
cout<<num<<endl;
}
else if(sub=="height")
{
//do stuff
}
else if (sub=="quit")
return mustquit;
//else cout<<"INPUT ERROR"<<endl;
} while (iss);
return mustquit;
}// process
int main(){
string input="";
CTree *myTree;
myTree = new CTree();
bool finished=false;
int i;
cout<<PROMPT;
while(!finished)
{
if(input!="")cout<<PROMPT;
getline(cin,input);
finished=process(input, myTree);
delete myTree;
}//while
return 0;
}
add is a non-static member function, which means you can only call it on an instance of CTree. e.g.
CTree myTree;
myTree.add(num);
You are aware that you need an instance of the class CTree to actually use it? You wrote the entire thing under the assumption that you're operating on an instance of a class. An actual tree, rather than a blueprint for it.
As the answer before me said, it's not a static function or class-level. A non-static method needs to be invoked on an instance so that a silent pointer this can be set to something meaningful, ie. the actual instance you're working with - in this case adding a node.
ADDENDUM
(everything below works without modifying your code, just an explicit answer to your question, to make it compile. From a "working standpoint", this program is far from complete. Some pieces don't even make sense, many variables are left unused or uninitialized (and then used). Let me elaborate further below.)
What you need to do is this add this in your main where the old process() call occured:
CTree myTree; // you could also add (), even though it's a default constructor
finished=process(input, myTree);
And modify the function process' argument list to include a reference to your tree which you wish to operate on. This is just one of the possibilities, you can also take a pointer etc. But a reference is cleaner:
bool process(const string& s, CTree& aTree)
Also, pay attention to compiler warnings. Good practice is to take care of all of them. And remember, this makes it compile, not work. It seems unfinished and rough around the edges.
And remember the difference between a class (an idea) and an instance (a manifestation of that idea). The technical details are not important right now, just make sure you have an instance to work with, as your class design intends. It seems to me that you don't have a grasp around how computer software works, how data and instructions that operate on it connect, especially from a viewpoint of memory. It's not enough for the computer to know what you want to do, it needs to know on what do you want the operations performed (which variables or objects or what-have-you). You can copy by value and return, do it in the main function, pass a reference or a pointer with an address so it can know where in memory is your object/instance located etc. If you're just experimenting, you could create a global instance. A lot of options.
Redeclaring everything doesn't carry over the changes that happen previously (since stuff goes out of scope). Nor does it make sense to call non-static member methods on the class level - and not even properly.
Hope it helps and happy coding. Keep at it, nothing worth doing is simple.
I think they are getting a little too technical for your level of experience. YourCTree class code creates what a CTree class is and how it behaves (a blueprint) but you actually have to tell your code to construct one and then have a way to reference it.
You can declare a stack variable instance like this:
CTree myTree;
This allocates the memory for your class and calls the constructor on entry into the function. You would then work with it by referencing the functions from the instance name using dot notation.
myTree.Add(4);
Or you can declare a pointer to a CTree and create a dynamic instance using the new operator
CTree *myTree;
myTree = new CTree();
Then you reference the tree using pointer notation:
myTree->Add(4);
if you do it that way you will need to delete the memory you allocated
delete myTree;
So in summary, a class definition of the kind you show here describes a class, but does not create one (allocate memory and setup pointers to the method code). This allows you to have many trees if your code logic requires them;
CTree directoryTree;
CTree fileTree;
CTree indexTree;
These would each have their own data ...
Good luck,
I'm a programming student in my first C++ class, and recently we covered linked lists, and we were given an assignment to implement a simple one. I have coded everything but my pop_back() function, which is supossed to return a pointer to the Node that needs to be deleted in Main(). No Node deletion is to be done in the actual function. So my question is:
Would you be willing to help point me in the right direction for my pop_back() function? Also, if you notice anything else that I'm doing wrong, let me know.
Also, this linked list is just to work with strings. In this case, a grocery list, so one string for the quantity of the item(1,2), and one string for the item type. (Milk, Eggs, etc.)
Below I've included my List & Node class implementations, so you can get an idea of what I've done so far.
Node.cpp
Node::Node(void)
{
descrip = " ";
quantity = " ";
previous = NULL;
next = NULL;
}
Node::Node(string q, string d)
{
descrip = d;
quantity = q;
previous = NULL;
next = NULL;
}
Node* Node::GetNext()
{
return next;
}
Node* Node::GetPrevious()
{
return previous;
}
void Node::SetNext(Node * setter)
{
next = setter;
}
void Node::SetPrevious(Node * setter)
{
previous = setter;
}
List.cpp
List::List(void)
{
first = NULL;
last = NULL;
numNodes = 0;
}
Node* List::GetFirst()
{
return first;
}
Node* List::GetLast()
{
return last;
}
void List::SetFirst(Node* setter)
{
first = setter;
}
void List::SetLast(Node* setter)
{
last = setter;
}
int List::GetNumNodes()
{
return numNodes;
}
void List::push_front(Node* item)
{
if (first == NULL)
{
first = item;
last = item;
}
else
{
Node* pFirst = first;
item->SetNext(pFirst);
first = item;
numNodes++;
}
}
void List::push_back(Node * item)
{
if (last == NULL)
{
first = item;
last = item;
}
else
{
last->SetNext(item);
last = item;
numNodes++;
}
}
Node* List::pop_front()
{
Node* temp = first;
first = first->GetNext();
if (first == NULL)
{
temp = first->GetNext();
first = p;
}
if (first == NULL)
{
last = NULL;
}
if (numNodes > 0)
{
numNodes--;
}
return temp;
}
Node* List::pop_back() // this whole function may be wrong, this is just my attempt at it
{
Node* temp;
temp = first;
while((temp->GetNext()) != NULL)
// im stuck here
}
Some pointers:
0x1243bfa3
0x45afc56e
0xdeadbeef
Some more pointers:
You should prefer to initialize your class members in the initialization list, not in the constructor's body.
In C++, unlike C89, we declare and define a function with no parameters as void f();, not void f(void);.
In C++ we commonly reset pointers with 0, not NULL.
See below for what I mean in code.
Good C++ code will try to take advantage of RAII. This implies avoiding primitive pointers for the most part. In this case plain old std::auto_ptr<> would make a perfectly sufficient substitute for the primitve Node* pointers. However, I do reckon part of the exercise here is pointer arithmetics, and so I just leave this as a side-note.
It would be useful for us if you'd attach the class declarations. I assumes all those accessors and mutators, GetFirst() and SetFirst() etc., are there because they are public. That's a bad idea. First, they expose the private pointers, which defeats the whole point of accessor. Second, they don't do anything special so they're just extra code -- which means extra room for bugs. This brings me to the next point.
Your mutators are incorrect. You blindly assign a new value to the private member pointer, without deleting what you had before. That's a memory leak.
Ever tried to pop_front() when the list is empty?
Finally, 8 being a round number it's time we get to the question at hand. pop_back(). My question to you is, why are you traversing the list all the way to the end if you so meticulously maintain a pointer to the last node of your list? Indeed, if you wouldn't bother with maintaining a pointer to the end of the list then you'd have to traverse all the way to the last node in order to pop it. And for that you were in the right direction. Except that ...
When you access members through pointers, as in first->GetNext(), always make sure first isn't a null pointer -- or else state in the function's documentation comment that you assume the pointer is not null.
These should get you started.
Points 1, 2 and 3 in code:
Node::Node()
: descrip(" "), quantity(" "), previous(0), next(0)
{
}
So if I understand this right you just want to run through your linked list until you get to the last node in the linked list and return the pointer to it?
I'm pretty sure what you have there will do it except
Node* List::pop_back() // this whole function may be wrong, this is just my attempt at it
{
Node* temp;
temp = first;
while(temp->GetNext() != NULL)
{
temp = temp->GetNext();
}
return temp;
}
So if I read it right, there it will continually loop around until it gets to the node with none in the line behind it, then return it.
I like the previous posters answer, but one thing you might want to keep in mind is if you have an empty list. Then your first pointer will equal NULL and you would be trying to call NULL->GetNext() basically and Seg Fault. I think you can edit the above code slightly and still get have it work like this:
Node* List::pop_back()
{
Node* temp;
temp = first;
while(temp != NULL && temp->GetNext() != NULL)
{
temp = temp->GetNext();
}
return temp;
}
This will have the function return NULL if there is nothing in the list and still work properly.
It would definitely have helped me if you also had posted your class declaration. I cannot guarantee that the below is correct but it makes sense to me
Node* List::pop_back()
{
Node *temp = NULL;
if(numNodes == 1)
{
temp = first;
// setting the list pointers to NULL
first = NULL;
// setting the list pointers to NULL
last = NULL;
//You should also probably remove the links from your node
//to the next and previous nodes but since you didn't specify
//this it is up to you
numNodes--;
}
else if(numNodes > 1) //more than one element
{
//the pointer you want to return
temp = last;
//For clarity I am creating another variable here
Node *newLast = temp->GetPrevious();
//Setting the new last node to point at nothing so now temp
//is "disconnected from the list"
newLast->next = NULL;
//the last pointer of the list is now pointing at the new last node
last = newLast;
//You should also probably remove the links from your node
//to the next and previous nodes but since you didn't specify this it is up to you
numNodes--; //decrement the counter
}
return temp;
}