I try to dynamically create graphs which can have unreachable nodes. These nodes will be converted as new graphs on creation. Then I save all graphs in a map of <string,shared_ptr>.
A MaterialArch is a graph
A MatArchNode is a node
Creation of the main graph :
void Console::newMaterial(std::string name, int size){
if(!isVariable(name)){
if(!inMaterial(name)){
std::shared_ptr<MaterialArch> mat = std::make_shared<MaterialArch>(m_matPop.get());
mat->generate(size);
m_matPop->add(name,mat);
}
}
else{
print("Error : Material name already used");
}
}
The generate function where unreachable nodes are converted to graph :
void MaterialArch::generate(int nbNodes){
std::vector<std::shared_ptr<MatArchNode>> nodeList;
if(m_root == nullptr){
m_root = std::make_shared<MatArchNode>();
m_root->generate();
nodeList.push_back(m_root);
for (int i = 1; i < nbNodes; ++i) {
std::shared_ptr<MatArchNode> nextNode = std::make_shared<MatArchNode>();
nextNode->generate();
nodeList.push_back(nextNode);
}
if(nodeList.size() < 2){
return;
}
for (auto node : nodeList) {
//linking
}
}
std::vector<std::shared_ptr<MatArchNode>> newRoots;
for (auto node : nodeList) {
bool unreachable = true;
if(!node->orpheline(m_root)){
unreachable = false;
}
for (auto root : newRoots) {
if(!node->orpheline(root)){
unreachable = false;
}
}
if(unreachable){
std::cout << "#ORPH" << std::endl;
std::shared_ptr<MaterialArch> orph = std::make_shared<MaterialArch>(m_matPop.get());
orph->setRoot(node);
m_pop->add(m_pop->orphName(),orph);
newRoots.push_back(node);
}
}
}
The function where I put the graph in the map :
void MaterialPop::add(std::string name, std::shared_ptr<MaterialArch> mat){
std::cout << "ADD : "<<name << std::endl;
bool alreadyExist = false;
for (auto var : m_varMaterials) {
if(var.second == mat){
alreadyExist = true;
}
}
if(!alreadyExist){
m_console->addMatName(name);
m_varMaterials.emplace(name,std::move(mat));
m_views.emplace(name, std::make_shared<MaterialArchView>(m_varMaterials.at(name)));
m_visibility.emplace(name,true);
}
}
My problem is as soon as I get out of generate(), every orph shared_ptr is empty. I thought it was because I try to access a deleted object but the main graph is created the same way, in a function, and is not empty out of it.
Thanks in advance for helping
Sorry I'm so bad, my setRoot() looked like :
if(!orpheline(node)){
m_root = node;
}
so the root wasn't set because node was always unreachable
Related
I'm trying to do the Maximum Sort of a Simple One-way List. However my program has some bugs in it, which I can't figure out what it is.
It behaves strange because sometimes it works well to a point where it just stop working but i see return 0 (for example: I give the list of 1234 and I get back 3412), sometimes it working to a point, then goes to an infinte loop. (for example: case of 12345 it put 5, 4 and 3 the the 1st place and then infinite loop).
The problem is probably in the list::remMax() or in the maxRend().
My code:
struct chain
{
char key;
chain *next;
};
/////////////////////////////////////////////
class list
{
private:
chain *L, **act;
public:
list() {
L=NULL;
act=&L;
}
~list() {
chain *p;
while(L) {
p=L;
L=L->next;
delete []p;
}
}
enum ERROR {endErr, memErr};
void first() {act=&L;};
void next() {
if(*act!=NULL) {
act=&(*act)->next;
} else throw endErr;
}
bool end() {return *act==NULL;}
bool oneEIn() {return L->next==NULL;} //list contains only 1 element
bool twoEIn() {return L->next->next==NULL;} //list contains 2 elements
void addE(char x) {
chain *p=new(nothrow) chain;
if(p == NULL) throw memErr;
p->key=x;
p->next=*act;
*act=p;
}
chain* remMax(chain *H) {
if(!oneEIn()) {
chain *qe, *q, *Mpe, *Mp;
if(twoEIn()) {
Mp = H;
q = Mp->next;
if(q->key > Mp->key) {
Mp->next=q->next;
return q;
} else {
H=q;
Mp->next=NULL;
return Mp;
}
} else {
Mp=H;
q=Mp->next;
Mpe = H;
qe = Mp;
while(q != NULL) {
if(Mpe->key > Mp->key) Mp = Mpe;
if(q->key > Mp->key) {
Mp=q;
Mpe=qe;
}
qe=q;
q=q->next;
}
if(Mpe == Mp) H=Mp->next;
else {
Mpe->next = Mp->next;
}
Mp->next=NULL;
return Mp;
}
} else {
chain *Mp;
Mp = H;
H = NULL;
return Mp;
}
}
void inE(chain *Mp) {
first();
Mp->next = *act;
addE(Mp->key);
}
chain* getFirst() {return L;}
void printList() {
chain *p=L;
while(p != NULL) {
putchar(p->key);
p=p->next;
}
}
};
///////////////////////////////////
void makeList(list& L) {
char c;
while((c=getchar())!='\n') {
L.addE(c);
L.next();
}
}
void maxRend(list& L) {
if(!L.oneEIn()) {
chain *H, *Mp;
H=L.getFirst();
while(H != NULL) {
cout<<"1.while\n";
Mp = L.remMax(H);
L.inE(Mp);
cout<<"putIn: "<<Mp->key<<endl;
H=H->next;
L.printList();
cout<<endl;
}
cout<<"\nSorted list: ";
L.printList();
} else L.printList();
}
//////////////////////////////////////////////
int main()
{
list L;
makeList(L);
maxRend(L);
return 0;
}
Here's my stack:
template <class Type>
struct Node {
Type value;
Node* next;
Node(Type n_value)
: value(n_value), next(nullptr)
{ }
static Node<Type>* Add(Node<Type>* head, const Type value) {
if (!head) { head = new Node<Type>(value); }
else { head->next = Add (head->next, value); }
return head;
}
static void Remove(Node<Type>* head) {
if (head->next) { Remove(head->next); }
else { delete head; }
}
static void Empty(Node<Type>* head) {
if (head->next) { Empty(head->next); }
delete head;
}
static void Display(const Node<Type>* head) {
std::cout << "\nBEGINNING\n";
std::function<void(const Node<Type>*)> DisplayElements = [&] (const Node<Type>* head) {
if (head) {
std::cout << head->value << std::endl;
if (head->next) { DisplayElements(head->next); }
}
};
DisplayElements(head);
std::cout << "\nEND\n";
}
};
With following code:
auto beg = Node<int>::Add(nullptr, 0);
for (int i = 0; i < 10; i++) { Node<int>::Add(beg, i); }
Node<int>::Display(beg);
Node<int>::Empty(beg);
Everything is okay:
BEGINNING
0
0
1
2
3
4
5
6
7
8
9
END
Program ended with exit code: 0
But when I try to remove something:
auto beg = Node<int>::Add(nullptr, 0);
for (int i = 0; i < 10; i++) { Node<int>::Add(beg, i); }
Node<int>::Remove(beg);
Node<int>::Remove(beg);
Node<int>::Display(beg);
Node<int>::Empty(beg);
return 0;
I get Pointer being freed was not allocated on delete head; in Remove. Why? It's absolutely the same as Empty (which erases whole list), except it deletes only last node. Empty works fine.
Empty is needed to delete whole list (so that we don't get memory leaks), and Remove is to remove last element.
I am unable to reproduce your results, so instead of an answer, I am providing a nudge on how you might proceed.
I changed the parameter of DisplayElements to 'lhead' (for local head), because the 'head' declaration shadow's the one in the outer scope. You also have some other shadows ... so I recommend you add -Wshadow to your compiler settings.
I also added to your Display() function some diagnostic hex output.
static void Display(const Node<Type>* head)
{
std::cout << "\nBEGINNING\n";
std::function<void(const Node<Type>*)> DisplayElements =
[&] (const Node<Type>* lhead)
{
if (lhead)
{ // vvvvvvvvvvvv--diagnostic only
std::cout << hex << lhead << " " << dec
<< lhead->m_value << std::endl;
if (lhead->m_next)
{
DisplayElements(lhead->m_next);
}
}
};
DisplayElements(head);
std::cout << "\nEND\n";
}
And in addition ... I changed the following. By avoiding the Empty() and the other couple of lines, the first display will more closely match the second:
auto beg = Node<int>::Add(nullptr, 0);
{
for (int i = 0; i < 10; i++) { Node<int>::Add(beg, i); }
Node<int>::Display(beg);
// Node<int>::Empty(beg);
}
{
// auto beg = Node<int>::Add(nullptr, 0);
// for (int i = 0; i < 10; i++) { Node<int>::Add(beg, i); }
Node<int>::Remove(beg);
Node<int>::Remove(beg);
Node<int>::Display(beg);
Node<int>::Empty(beg);
}
Now consider connecting the debugger and setting a break point to inspect what "Remove()" is doing.
Don't be shy about adding more diagnostic cout's.
What I notice is:
a) that the last lines of output are an infinite loop.
b) outputs for values 1..8 are identical to the first,
c) the output for value 9 is missing ... and I suppose should be ... but something about that last Node (perhaps the one with 8?) must be confused.
So I've been working on a project for school for sometime, and I've run up against a wall. My add_node function isn't working correctly, and I know why. What I'm trying to do is take in a file with multiple randomly generated letters, and create trees out of them, then make a confirmation.
The thing is that it overwrites the same node, instead of making multiple nodes. I figured this out using Visual studios debugger, but I have no idea what to implement to fix it. What happens is that instead of having multiple nodes create a tree (like gagtttca), it makes one node and overwrites it. The node becomes g, then a, etc. How would I go about adding more nodes to the tree without overwriting it? The add_node function is the very last one.
#include "stdafx.h"
#include <iostream>
#include <stack>
#include <fstream>
#include <vector>
#include <cstring>
#include <string>
using namespace std;
class myTreeNode
{
public:
char Data;
myTreeNode *childA; //A's always go in child1
myTreeNode *childT; //T's always go in child2
myTreeNode *childC; //c's always go in child3
myTreeNode *childG; //G's always go in child4
};
class Tree
{
public:
myTreeNode * Root;
Tree()
{
Root = new myTreeNode;
Root->Data = '-';
Root->childA = Root->childC = Root->childG = Root->childT = NULL;
}
bool add_a_word(string word);
bool is_this_word_in_the_tree(string word);
bool add_node(myTreeNode * parent, char letter);
bool add_words(vector<string> w);
};
bool get_words_from_the_file(char * my_file_name, vector<string> &vector_of_words);
bool get_the_reads_from_file(char * my_file_name, vector<string> &reads);
bool write_out_the_vector_to_screen(vector<string> my_vector);
bool write_out_the_vector_to_file(vector<string> my_vector, char * my_file_name);
ofstream out;
int main()
{
out.open("my_results.txt");
vector<string> words_in_genome;
char * genome_file_name = "my_genome.txt";//make certain to place this file in the correct folder. Do not change path.
if (!get_words_from_the_file(genome_file_name, words_in_genome))
return 1;
Tree * trees = new Tree();
trees->add_words(words_in_genome);
char * reads_file_name = "reads.txt"; //make certain to place this file in the correct folder. Do not change path.
if (!get_the_reads_from_file(reads_file_name, reads_to_be_tested))
return 1;
for (int i = 0; i < reads_to_be_tested.size(); i++)
{
out <<reads_to_be_tested[i] << " " << trees->is_this_word_in_the_tree(reads_to_be_tested[i]);
}
cout << "All done" << endl;
//Write out a file named "myResults.txt".
//For each read, list its sequence and either "Yes" or "No".
//This will indicate if it does or doesn't map to the genome.
/** Used for debugging
cout << "words" << endl;
write_vector_to_screen(words);
write_vector_to_file(words,"testing.txt");
cout << "reads" << endl;
write_vector_to_screen(reads);
***/
out.close();
}
bool get_words_from_the_file(char * my_file_name, vector<string> &vector_of_words)
{
int i, j;
int len = 0;
ifstream in;
in.open(my_file_name);
if (!in.is_open())
{
cout << "I could not find " << my_file_name << endl;
cout << "Check the location.\n";
return false;
}
char * my_word = new char[11];
while (in.peek() != EOF) { in >> my_word[0]; len++; }
in.clear(); in.close(); in.open(my_file_name);
for (i = 0; i<10; i++)
{
in >> my_word[i];
if (my_word[i]<97) my_word[i] += 32; //makes it lowercase
}
my_word[10] = '\0';
vector_of_words.push_back(my_word);
for (i = 1; i<(len - 10 - 1); i++) //read until the end of the file
{
//shift
for (j = 0; j<9; j++) my_word[j] = my_word[j + 1];
in >> my_word[9];
if (my_word[9]<97) my_word[9] += 32; //makes it lowercase
my_word[10] = '\0';
cout << i << "\t" << my_word << endl; cout.flush();
vector_of_words.push_back(my_word);
}
in.clear(); in.close();
return true;
}
bool get_the_reads_from_file(char * my_file_name, vector<string> &reads)
{
int i;
ifstream in;
in.open(my_file_name);
if (!in.is_open())
{
cout << "The read file " << my_file_name << " could not be opened.\nCheck the location.\n";
return false;
}
char * word = new char[20]; //this is a default, we'll be looking at words of size 10
while (in.peek() != EOF)
{
in.getline(word, 20, '\n');
for (i = 0; i<10; i++) { if (word[i]<97) word[i] += 32; } //makes it lowercase
reads.push_back(word);
}
in.clear(); in.close();
delete word;
return true;
}
bool write_out_the_vector_to_screen(vector<string> my_vector)
{
int i;
for (i = 0; i < my_vector.size(); i++)
{
cout << my_vector[i].c_str() << endl;
}
return true;
}
bool write_out_the_vector_to_file(vector<string> my_vector, char * my_file_name)
{
ofstream out;
out.open(my_file_name);
int i;
for (i = 0; i<my_vector.size(); i++)
out << my_vector[i].c_str()<< endl;
out.clear();
out.close();
return true;
}
bool Tree::add_words(vector<string> w)
{
for (int i = 0; i < w.size(); i++)
add_a_word(w[i]);
return true;
}
bool Tree::add_a_word(string word)
{
myTreeNode * tempNode = new myTreeNode;
tempNode = Root;
if (tempNode == NULL)
{
cout << "The tree is empty" << endl;
}
else
{
while (tempNode != NULL)
{
for (int i = 0; i < word.size(); i++)
{
if (word[i] == 'a')
{
if (tempNode->childA != NULL)
tempNode = tempNode->childA;
else
{
add_node(tempNode, word[i]);//add a node: what letter, who's my parent
tempNode = tempNode->childA;
}
}
else if (word[i]== 'g')
{
if (tempNode->childG != NULL)
tempNode = tempNode->childG;
else
{
add_node(tempNode, word[i]);
tempNode = tempNode->childG;
}
}
else if (word[i] == 'c')
{
if (tempNode->childC != NULL)
tempNode = tempNode->childG;
else
{
add_node(tempNode, word[i]);
tempNode = tempNode->childC;
}
}
else if (word[i] == 't')
{
if (tempNode->childT != NULL)
tempNode = tempNode->childT;
else
{
add_node(tempNode, word[i]);
tempNode = tempNode->childT;
}
}
else
{
cout << "The tree is full, or can't find data" << endl;
return NULL;
break;
}
}
}
}
}
bool Tree::is_this_word_in_the_tree(string word)
{
myTreeNode * tempNode = new myTreeNode;
tempNode = Root;
char com1, com2, com3, com4;
if (tempNode == NULL)
{
cout << "The tree is empty. Sorry" << endl;
}
else
{
while (tempNode != NULL)
{
for (int i = 0; i < word.size(); i++)
{
if (word[i] == 'a')
{
if (tempNode->childA != NULL)
{
if (tempNode->childA)
{
tempNode = tempNode->childA;
com1 = 'y';
}
}
else
{
com1 = 'n';
}
}
if (word[i] == 'g')
{
if (tempNode->childG != NULL)
{
if (tempNode->childG)
{
tempNode = tempNode->childG;
com2 = 'y';
}
}
else
{
com2 = 'n';
}
}
if (word[i] == 't')
{
if (tempNode->childT != NULL)
{
if (tempNode->childT)
{
tempNode = tempNode->childG;
com3 = 'y';
}
}
else
{
com3 = 'n';
}
}
if (word[i] == 'c')
{
if (tempNode->childC != NULL)
{
if (tempNode->childC)
{
tempNode = tempNode->childC;
com4 = 'y';
}
}
else
{
com4 = 'n';
}
}
}
out << com1 << com2 << com3 << com4 << endl;
if (com1 == com2 == com3 == com4)
{
out << "The test passed" << endl;
}
else
{
out << "The test failed" << endl;
return false;
}
}
}
return true;
}
bool Tree::add_node(myTreeNode * parent, char letter)
{
//Can't figure out how to fix error. Run-Time error is that it overwrites the node instead of adding it.
//How would i make it so it's a new node every time?//
myTreeNode * tempNode = new myTreeNode;
tempNode = Root;
tempNode->Data = letter;
tempNode->childA = tempNode->childC = tempNode->childG = tempNode->childT = NULL;
if (tempNode == NULL)
{
cout << "The tree is empty" << endl;
}
else
{
while (tempNode != NULL)
{
if (parent->childA == NULL && letter =='a')
{
parent->childA = tempNode;
}
else if (parent->childC == NULL && letter == 'c')
{
parent->childC = tempNode;
}
else if (parent->childG == NULL && letter == 'g')
{
parent->childG = tempNode;
}
else if (parent->childT == NULL && letter == 't')
{
parent->childT = tempNode;
}
else
{
cout<<"no"<<endl; //for testing//
return false;
break;
}
}
}
return true;
}
Like I stated before, this is a project. I'm not here looking for an easy way out. I just want learn how to fix my code.
The most fundamental problem in your code is the simple obviousness that you're not comfortable using pointers. From the looks of it you may have come from other languages where the vernacular of:
Type *p = new Type;
p = Something;
was common. It is anything-but-common in C++. As in C, dynamic allocation is managed by a returned address, which is saved, cared for, and if all goes well, eventually disposed of. Those addresses are kept in pointer variables. Pointers in C++ don't hold objects; they hold addresses.
That said, I'm not going to destroy everything you wrote. I'm not going to sugar coat this; it would be shooting fish in a barrel. I'm rather going to describe what you should be doing in add_node, show you where you went wrong, and finally proffer up a simple example that eliminates much of the cruft (file io, etc) in your existing code, focusing rather on the real problem at hand: tree node management and the pointer-jockeying that is needed to accomplish it.
The Task
You should be starting at the root node, and for each successive letter in your string, move down the tree. When you encounter a path you want to take, but can't because there is no node hanging there yet, that is when you allocate a new node, hang it, move to it, and continue the process until there are no more characters in your input string.
Your Code
That said, review the comments in the following
bool Tree::add_node(myTreeNode * parent, char letter)
{
myTreeNode * tempNode = new myTreeNode;
// this is outright wrong. you just leaked the memory
// you allocated above. this has no place here and
// should be removed.
//
// Note: the remainder of this analysis will assume you
// have, in fact, removed this line.
tempNode = Root;
// all of this belongs in your myTreeNode constructor.
tempNode->Data = letter;
tempNode->childA = tempNode->childC = tempNode->childG = tempNode->childT = NULL;
// this is flat-out impossible. Assuming you fixed your incorrect
// Root assignment mentioned above, you just allocated a new node
// therefore this can NEVER be NULL (an exception would have thrown
// on a failure to allocate).
if (tempNode == NULL)
{
cout << "The tree is empty" << endl;
}
else
{
// This NEVER changes. Nowhere in the code below this is
// tempNode ever assigned a different value. this loop
// should not even be here. A simple if-else-if stack or
// a switch on letter is all that is needed.
while (tempNode != NULL)
{
if (parent->childA == NULL && letter =='a')
{
parent->childA = tempNode;
}
else if (parent->childC == NULL && letter == 'c')
{
parent->childC = tempNode;
}
else if (parent->childG == NULL && letter == 'g')
{
parent->childG = tempNode;
}
else if (parent->childT == NULL && letter == 't')
{
parent->childT = tempNode;
}
else
{
cout<<"no"<<endl; //for testing//
return false;
break;
}
}
}
return true;
}
Sample Code
The following strips out all the file io, and most of the insanity regarding managing the tree. There are only two member functions, add_word and has_word (the latter used to validate something is indeed present).
What makes this code work is how a pointer-to-pointer is used in the addition and check functions add_word and has_word. For addition, we start at the root node pointer, and with each successive character in the input string, move down the tree. When a child pointer is hit that is NULL, we allocate a new node, hang it, and move on. The check function has_word does exactly the same thing, save for one difference: it doesn't hang new nodes. When it encounters a NULL where there shouldn't be one, it means something is wrong and the input word is not in the tree.
#include <iostream>
#include <random>
#include <string>
struct myTreeNode
{
char data;
myTreeNode *childA;
myTreeNode *childT;
myTreeNode *childC;
myTreeNode *childG;
myTreeNode( char c )
: data(c), childA(), childT(), childC(), childG()
{
}
~myTreeNode()
{
delete childA;
delete childT;
delete childC;
delete childG;
}
// squelch these
myTreeNode(const myTreeNode&) = delete;
myTreeNode& operator=(const myTreeNode&) = delete;
};
class Tree
{
private:
myTreeNode *Root;
public:
Tree() : Root( new myTreeNode('-')) { }
~Tree() { delete Root; }
// squelch these
Tree(const Tree&) = delete;
Tree& operator =(const Tree&) = delete;
// adds a given string into the tree if it isn't already there.
void add_word(const std::string& word)
{
myTreeNode **pp = &Root;
for (auto c : word)
{
c = std::tolower((unsigned int)c);
switch(c)
{
case 'a':
pp = &(*pp)->childA;
break;
case 't':
pp = &(*pp)->childT;
break;
case 'c':
pp = &(*pp)->childC;
break;
case 'g':
pp = &(*pp)->childG;
break;
default:
std::cerr << "skipping unsupported char '" << c << "'\n";
}
if (!*pp)
*pp = new myTreeNode(c);
}
}
// returns true if the given string is in the tree
bool has_word(const std::string& word)
{
myTreeNode **pp = &Root;
for (auto c : word)
{
c = std::tolower((unsigned int)c);
switch(c)
{
case 'a':
pp = &(*pp)->childA;
break;
case 't':
pp = &(*pp)->childT;
break;
case 'c':
pp = &(*pp)->childC;
break;
case 'g':
pp = &(*pp)->childG;
break;
default: // should never happen with proper input
return false;
}
if (!*pp)
return false;
}
return true;
}
};
////////////////////////////////////////////////////////////////////////////////
int main()
{
// setup a random device and some uniform distributions
std::random_device rd;
std::mt19937 rng(rd());
std::uniform_int_distribution<> dchar(0,3);
std::uniform_int_distribution<> dlen(3,8);
// our restricted alphabet. random indexes for creating our
// strings will be coming by indexing with dchar(rng)
char s[] = {'a', 't', 'c', 'g' };
// build set of random strings
std::vector<std::string> strs;
for (int i=0; i<20; ++i)
{
std::string str;
int len = dlen(rng);
for (int j=0; j<len; ++j)
str.push_back(s[dchar(rng)]); // push random char
strs.emplace_back(str);
}
// drop list of strins into tree
Tree tree;
for (auto const& str : strs)
{
std::cout << str << '\n';
tree.add_word(str);
}
// now verify every string we just inserted is in the tree
for (auto const& str : strs)
{
if (!tree.has_word(str))
{
std::cerr << "Word \"" << str << "\" should be in tree, but was NOT\n";
std::exit(EXIT_FAILURE);
}
}
std::cout << "All test words found!!\n";
return EXIT_SUCCESS;
}
Output (varies due to random generators)
gctccgga
agtccatt
gagcg
gtggg
tca
aga
cacaggg
cga
tgga
ttatta
cagg
aac
tatttg
gccttat
acctcca
tgagac
aagacg
tgc
aaccgg
tca
All test words found!!
Summary
I strongly advise you run this in the debugger and step through it with a firm grasp on the watch-window. Follow pointer trails to see how things set up as the program progresses. There are many things I did not talk about: proper construction, initialization, Rule of Three compliance etc. I also could have (and would have had this not been an academic case) used smart pointers such as std::unique_ptr<> or std::shared_ptr<>. I sincerely hope you get something out of this. It's only going to get worse from here.
Best of luck
I don't know why but
this :
Root->childA = Root->childC = Root->childG = Root->childT = NULL;
Doesn't look right for me, haven't done c++ for a while and nodes but i don't think that's how you gotta do it? Will check and edit this.
I'm trying to implement AVL Tree in C++, but I'm stuck with the insertion, I have changed some things but nothing seemed to effectively solve the problem. I used Xcode's Address Sanitizer and I'm getting that error after inserting a second element into the tree:
Thread 1: Use of deallocated memory detected.
==3822==ERROR: AddressSanitizer: heap-use-after-free on address.....
This is the implementation of the tree so far:
RoadTree.hpp
#ifndef RoadTree_hpp
#define RoadTree_hpp
#include "Road.hpp"
class RoadTree {
private:
struct TreeNode {
Road *key;
TreeNode *rightChild;
TreeNode *leftChild;
int height;
TreeNode() : key(NULL), rightChild(NULL), leftChild(NULL), height(0) { }
TreeNode(Road *r) : key(r), rightChild(NULL), leftChild(NULL), height(0) { }
};
TreeNode *root;
int numberOfRoads;
int GetHeight(TreeNode *n) const;
void SimpleRightRotation(TreeNode *&n);
void DoubleRightRotation(TreeNode *&n);
void SimpleLeftRotation(TreeNode *&n);
void DoubleLeftRotation(TreeNode *&n);
void Insert(TreeNode *&n, Road *r);
void ClearTree(TreeNode *&n);
void PreOrder(TreeNode *n) const;
public:
RoadTree();
~RoadTree();
void Insert(Road *r);
Road *FindRoad(string destination);
void ListRoads();
void ClearTree();
void PreOrder();
inline int RoadCount() {
return numberOfRoads;
}
};
#endif /* RoadTree_hpp */
RoadTree.cpp
#include "RoadTree.hpp"
RoadTree::RoadTree() : root(NULL), numberOfRoads(0) { }
RoadTree::~RoadTree() {
ClearTree(root);
}
void RoadTree::Insert(Road *r) {
Insert(root, r);
}
int RoadTree::GetHeight(TreeNode *n) const {
if (n == NULL)
return -1;
else
return n->height;
}
void RoadTree::SimpleRightRotation(TreeNode *&n) {
TreeNode *tempNode = n->rightChild;
n->rightChild = tempNode->leftChild;
tempNode->leftChild = n;
n->height = 1 + max(GetHeight(n->leftChild), GetHeight(n->rightChild));
n = tempNode;
tempNode->height = 1 + max(n->height, GetHeight(tempNode->rightChild));
}
void RoadTree::DoubleRightRotation(TreeNode *&n) {
SimpleLeftRotation(n->rightChild);
SimpleRightRotation(n);
}
void RoadTree::SimpleLeftRotation(TreeNode *&n) {
TreeNode *tempNode = n->leftChild;
n->leftChild = tempNode->rightChild;
tempNode->rightChild = n;
n->height = 1 + max(GetHeight(n->leftChild), GetHeight(n->rightChild));
n = tempNode;
tempNode->height = 1 + max(n->height, GetHeight(n->leftChild));
}
void RoadTree::DoubleLeftRotation(TreeNode *&n) {
SimpleRightRotation(n->leftChild);
SimpleLeftRotation(n);
}
void RoadTree::ClearTree(TreeNode *&n) {
if (n != NULL) {
ClearTree(n->rightChild);
ClearTree(n->leftChild);
delete n;
}
n = NULL;
}
void RoadTree::Insert(TreeNode *&n, Road *r) {
if (n == NULL) {
n = new TreeNode(r);
numberOfRoads++;
} else {
if (r->GetDestination() < n->key->GetDestination()) {
Insert(n->leftChild, r);
if ((GetHeight(n->leftChild) - GetHeight(n->rightChild)) == 2) {
if (r->GetDestination() < n->leftChild->key->GetDestination())
SimpleLeftRotation(n);
else
DoubleLeftRotation(n);
}
} else if (r->GetDestination() > n->key->GetDestination()) {
Insert(n->rightChild, r);
if ((GetHeight(n->rightChild) - GetHeight(n->leftChild)) == 2) {
if (r->GetDestination() > n->rightChild->key->GetDestination())
SimpleRightRotation(n);
else
DoubleRightRotation(n);
}
} else if (r->GetDestination() == n->key->GetDestination())
n->key->SetRoad(r->GetDestination(), r->GetCost(), r->GetInfo());
}
n->height = 1 + max(GetHeight(n->leftChild), GetHeight(n->rightChild));
}
Road *RoadTree::FindRoad(string destination) {
TreeNode *n = root;
while (n != NULL) {
string current = n->key->GetDestination();
if (destination < current)
n = n->leftChild;
else if (destination > current)
n = n->rightChild;
else if (destination == current)
return n->key;
}
return NULL;
}
void RoadTree::PreOrder(TreeNode *n) const {
if (n != NULL) {
cout << " " << n->key->GetDestination() << " ";
PreOrder(n->leftChild);
PreOrder(n->rightChild);
}
}
void RoadTree::PreOrder() {
PreOrder(root);
}
void RoadTree::ListRoads() {
}
void RoadTree::ClearTree() {
ClearTree(root);
}
And this is the implementation of Road:
Road.hpp
#ifndef Road_hpp
#define Road_hpp
#include <iostream>
using namespace std;
class Road {
private:
string destination;
int cost;
string info;
public:
Road();
Road(string destination, int cost, string info);
inline string GetDestination() {
return destination;
}
inline int GetCost() {
return cost;
}
inline string GetInfo() {
return info;
}
};
#endif /* Road_hpp */
Road.cpp
#include "Road.hpp"
Road::Road() {
destination = "";
cost = 0;
info = "";
}
Road::Road(string destination, int cost, string info) {
this->destination = destination;
this->cost = cost;
this->info = info;
}
The only way I can insert more than 1 element is leaving the destructor blank, then no error shows, so I don't know what's causing it to fail. The error is showing up at the Insertion method, in the line that compares the elements in order to advance in the tree.
Update: Since this is part of a bigger project, I'm almost 100% sure that the problem isn't from the tree's implementation (I put the tree and Road class in a separate project and everything worked as intended). The full project has a class called Place, it has a name and info, as well as an AVL Tree for each place (where I store the place's roads). Those places are stored in a Hash Table (that I have implemented myself).
This is the implementation of the Place class:
Place.hpp
#ifndef Place_hpp
#define Place_hpp
#include <iostream>
#include "Road.hpp"
#include "RoadTree.hpp"
using namespace std;
class Place {
private:
string name;
string info;
RoadTree adjacentRoads;
public:
Place();
Place(string name, string info);
void InsertRoad(Road *r);
Road *FindRoad(string destination);
void ListRoads();
inline string GetName() {
return name;
}
inline string GetInfo() {
return info;
}
inline void SetPlace(string newName, string newInfo) {
name = newName;
info = newInfo;
}
inline void Write() {
cout << name << endl;
cout << "Info: " << info << endl;
}
};
Place.cpp
#include "Place.hpp"
Place::Place() {
name = "";
info = "";
}
Place::Place(string name, string info) {
this->name = name;
this->info = info;
}
void Place::InsertRoad(Road *r) {
adjacentRoads.Insert(r);
}
Road *Place::FindRoad(string destination) {
return adjacentRoads.FindRoad(destination);
}
void Place::ListRoads() {
adjacentRoads.ListRoads();
}
This is how I get a pointer from the Hash Table (if the full code is needed tell me):
Place *HashTable::Find(string key) {
unsigned long hashedKey = HashFunction(key);
list<Place>::iterator current;
for (current = table[hashedKey].begin(); current != table[hashedKey].end(); current++) {
Place currentPlace = *current;
if (currentPlace.GetName() == key)
return &*current;
}
return NULL;
}
And this is an example of a main that gives me the Thread 1: Use of deallocated memory detected. error
int main(int argc, const char * argv[]) {
//Declare a HashTable to store Places
HashTable map;
//Declare some places
Place p1("Murcia", "10");
Place p2("Lorca", "11");
Place p3("Cartagena", "12");
Place p4("Zaragoza", "13");
Place p5("Madrid", "14");
Place p6("Galicia", "15");
//Insert those places into the HashTable
map.Insert(p1);
map.Insert(p2);
map.Insert(p3);
map.Insert(p4);
map.Insert(p5);
map.Insert(p6);
//Declare some roads
Road *r1 = new Road(p2.GetName(), 20, "asdgasdg");
Road *r2 = new Road(p3.GetName(), 61, "asdgsw2");
//Get a pointer of a place from the HashTable to insert roads in it
Place *p1f = map.Find(p1.GetName());
//Check if it's not null, if it's not then insert the first road,
//get a pointer of it and print the name
if (p1f != NULL) {
p1f->InsertRoad(r1);
Road *r1f = p1f->FindRoad(p2.GetName());
cout << r1f->GetDestination() << endl;
}
//Get pointer of a place again (each time you want to insert a road
//in a place you must get it's pointer from the HashTable
Place *p2f = map.Find(p1.GetName());
//Checks again and insert second road, then throws error after that
if (p2f != NULL) {
p2f->InsertRoad(r2);
Road *r2f = p1f->FindRoad(p3.GetName());
cout << r2f->GetDestination() << endl;
}
return 0;
Update 2: Added HashTable implementation
HashTable.hpp
#ifndef HashTable_hpp
#define HashTable_hpp
#include "Place.hpp"
#include <list>
class HashTable {
private:
list<Place> *table;
int numberOfEntries;
int currentTableSize;
float maxLoadFactor;
unsigned int HashFunction(string key);
bool LoadFactorExceeded();
void ResizeTable();
bool IsPrime(int number);
int NextPrime(int number);
public:
HashTable();
~HashTable();
void Insert(Place p);
Place *Find(string key);
void EmptyTable();
void ListPlaces();
inline int Count() {
return numberOfEntries;
}
};
#endif /* HashTable_hpp */
HashTable.cpp
#include "HashTable.hpp"
#include <algorithm>
const int START_SIZE = 101;
HashTable::HashTable() {
table = new list<Place>[START_SIZE];
numberOfEntries = 0;
maxLoadFactor = 2.0f;
currentTableSize = START_SIZE;
for (int i = 0; i < START_SIZE; i++) {
table[i].clear();
}
}
HashTable::~HashTable() {
delete [] table;
}
unsigned int HashTable::HashFunction(string key) {
unsigned long hashValue = 0;
for (int i = 0; i < key.length(); i++)
hashValue = 47 * hashValue + key[i];
return (hashValue % currentTableSize);
}
bool HashTable::LoadFactorExceeded() {
float currentLoadFactor = numberOfEntries / currentTableSize;
if (currentLoadFactor > maxLoadFactor)
return true;
else
return false;
}
void HashTable::ResizeTable() {
list<Place> *oldTable = table;
int oldTableSize = currentTableSize;
currentTableSize *= 2;
currentTableSize = NextPrime(currentTableSize);
table = new list<Place>[currentTableSize];
for (int i = 0; i < currentTableSize; i++)
table[i].clear();
numberOfEntries = 0;
for (int i = 0; i < oldTableSize; i++) {
list<Place>::iterator current;
for (current = oldTable[i].begin(); current != oldTable[i].end(); current++)
Insert(*current);
}
delete [] oldTable;
}
bool HashTable::IsPrime(int number) {
if (number % 2 == 0 || number % 3 == 0)
return false;
int divisor = 6;
while (divisor * divisor - 2 * divisor + 1 <= number) {
if (number % (divisor - 1) == 0)
return false;
if (number % (divisor + 1) == 0)
return false;
divisor += 6;
}
return true;
}
int HashTable::NextPrime(int number) {
while (!IsPrime(++number)) {}
return number;
}
void HashTable::Insert(Place p) {
unsigned long hashedKey = HashFunction(p.GetName());
list<Place>::iterator current = table[hashedKey].begin();
if (!table[hashedKey].empty()) {
for (current = table[hashedKey].begin(); current != table[hashedKey].end(); current++) {
Place ¤tPlace = *current;
if (currentPlace.GetName() == p.GetName()) {
currentPlace.SetPlace(p.GetName(), p.GetInfo());
break;
} else if (current == --table[hashedKey].end()) {
table[hashedKey].push_back(p);
numberOfEntries++;
}
}
} else {
table[hashedKey].push_back(p);
numberOfEntries++;
}
if (LoadFactorExceeded())
ResizeTable();
}
Place *HashTable::Find(string key) {
unsigned long hashedKey = HashFunction(key);
list<Place>::iterator current;
for (current = table[hashedKey].begin(); current != table[hashedKey].end(); current++) {
Place currentPlace = *current;
if (currentPlace.GetName() == key)
return &*current;
}
return NULL;
}
void HashTable::EmptyTable() {
for (int i = 0; i < currentTableSize; i++) {
table[i].clear();
}
table = new list<Place>[START_SIZE];
numberOfEntries = 0;
currentTableSize = START_SIZE;
}
void HashTable::ListPlaces() {
list<string> places;
for (int i = 0; i < currentTableSize; i++) {
list<Place>::iterator current;
for (current = table[i].begin(); current != table[i].end(); current++)
places.push_back(current->GetName());
}
places.sort();
for (list<string>::iterator current = places.begin(); current != places.end(); current++)
cout << *current << endl;
cout << "Total: " << numberOfEntries << " lugares" << endl;
}
What could be causing the problem?
I'm not sure if this is it, but I noticed something: it looks like a linked list, and your recursive ClearTree function will attempt to free items repeatedly:
void RoadTree::ClearTree(TreeNode *&n) {
if (n != NULL) {
ClearTree(n->rightChild);
ClearTree(n->leftChild);
delete n;
}
n = NULL;
}
Assuming there are 2 elements in the list, and we call it with the first element:
ClearTree( firstElement );
It will then first
ClearTree(n->rightChild); // 2nd element
which in turn will first call
ClearTree(n->rightChild); // non-existing 3rd element: NOP
and proceed with
ClearTree(n->leftChild); // first element again
Maybe if you didn't get the error, this would recurse until you get a stack overflow?
You could simply remove the call to ClearTree(n->leftChild) to fix it; the function will recurse across the rightChild until it reaches the end, then delete the nodes from last to first when it backtracks.
Perhaps it's better to just iterate over the list: (untested, hope this works)
TreeNode * cur = n;
while ( cur != NULL )
TreeNode * next = n->rightChild;
delete cur;
cur = next;
}
n = NULL;
UPDATE
I've found the problem. Here's my debug output:
> g++ -O0 -g *cpp && gdb ./a.out
(gdb) r
Starting program: /home/kenney/roadtree/a.out
= INITIALIZING PLACES =
--> RoadTree[0x7fffffffe1a0] CONSTRUCTOR root: 0
--> RoadTree[0x7fffffffe1c0] CONSTRUCTOR root: 0
--> RoadTree[0x7fffffffe1e0] CONSTRUCTOR root: 0
--> RoadTree[0x7fffffffe200] CONSTRUCTOR root: 0
--> RoadTree[0x7fffffffe220] CONSTRUCTOR root: 0
--> RoadTree[0x7fffffffe240] CONSTRUCTOR root: 0
= INSERTING PLACES =
<-- RoadTree[0x7fffffffe340] DESTRUCTOR! root: 0
<-- RoadTree[0x7fffffffe360] DESTRUCTOR! root: 0
<-- RoadTree[0x7fffffffe380] DESTRUCTOR! root: 0
<-- RoadTree[0x7fffffffe3a0] DESTRUCTOR! root: 0
<-- RoadTree[0x7fffffffe3c0] DESTRUCTOR! root: 0
<-- RoadTree[0x7fffffffe3e0] DESTRUCTOR! root: 0
= CREATING ROADS =
These are the p1..p6 and the map.Insert(p1..p6). There's already a hint that something is wrong. Next this code is run:
cout << "= p1 =\n";
Place *p1f = map.Find(p1.GetName());
cout << "found " << p1f << " for " << p1.GetName() << "\n";
Producing this debug output:
= p1 =
<-- RoadTree[0x7fffffffe110] DESTRUCTOR! root: 0
found 0x6098f0 for Murcia
Then,
if (p1f != NULL) {
p1f->InsertRoad(r1);
Road *r1f = p1f->FindRoad(p2.GetName());
cout << r1f->GetDestination() << endl;
}
outputting this debug from RoadTree::Insert, indicating that the first if statement's 'then' is executed, assigning a new TreeNode to n:
n null, allocating.
--> TreeNode[0x609ad0] CONSTRUCTOR
allocated TreeNode 0x609ad0 key: 0x609a60 dest: Lorca
Lorca
So far so good, now the same again for p2. First the output of map.Find:
= p2 =
FINDING Murcia
<-- RoadTree[0x7fffffffe110] DESTRUCTOR! root: 0x609ad0
!!! RoadTree::ClearTree:: delete 0x609a60
<-- TreeNode[0x609ad0] DESTRUCTOR
found 0x6098f0 for Murcia
Next we continue to p2f->InsertRoad(r2); which is basically Place.adjacentroads.Insert aka RoadTree.insert:
n not null: 0x609ad0 key: 0x609af0
Note the address of n: this is the deleted TreeNode.
Here, the 'else' of the 'if' in RoadTree::Insert is taken since n != NULL:
if (r->GetDestination() < n->key->GetDestination()) {
is executed, causing:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7b9126b in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string const&) ()
from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
(gdb) bt
#0 0x00007ffff7b9126b in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string const&) ()
from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#1 0x00000000004046b3 in Road::GetDestination (this=0x609af0) at Road.hpp:20
#2 0x0000000000405121 in RoadTree::Insert (this=0x609900, n=#0x609900: 0x609ad0, r=0x609ab0) at RoadTree.cpp:75
#3 0x0000000000404c0d in RoadTree::Insert (this=0x609900, r=0x609ab0) at RoadTree.cpp:15
#4 0x0000000000404845 in Place::InsertRoad (this=0x6098f0, r=0x609ab0) at Place.cpp:14
#5 0x000000000040401d in main (argc=1, argv=0x7fffffffe5f8) at main.cpp:63
(gdb)
The fault is apparent in the n->key->GetDestination() which attempts to return a copy of a string that is already deleted, causing a segfault because some pointers are already overwritten.
The problem is in HashTable::Find, which does this:
Place currentPlace = *current;
if (currentPlace.GetName() == key)
return &*current;
which constructs a Place copy on the stack that gets destroyed when the method returns. The private fields of Place also get destroyed, including the string name, which was attempted to be returned by Road::GetDestination().
Replacing it with this with this solves it:
if (current->GetName() == key)
return &*current;
I'm not sure this is the only fix needed, but it's a step.
I tried to strictly implement in c++ what I'm studying in algorithmic at the moment, recursive functions with simple linked lists. Here is what I've come by :
#include <iostream>
using namespace std;
class Liste {
private :
int val;
Liste *suivante;
public :
Liste(int val = 0, Liste *suivante = NULL) {
this->val = val;
this->suivante = suivante;
}
void afficherElement() const {
cout << "Adresse : " << this << endl;
cout << "Valeur : " << val << endl;
cout << "Adresse suivante : " << suivante << endl;
cout << endl;
}
int tete() const {
return val;
}
Liste reste() const {
return *suivante;
}
bool estVide() const {
return (suivante == NULL);
}
Liste prefixer(int val) {
Liste *nouvelle = new Liste(val, this);
return *nouvelle;
}
Liste suffixer(int val) {
suivante = new Liste(val);
afficherElement(); // test (last element won't be displayed)
return *suivante;
}
};
int main() {
Liste uneListe(3); // 1st element
uneListe.suffixer(5).suffixer(8).suffixer(10); // adding 3 more
cout << "-----------\n\n";
uneListe.afficherElement(); // displaying 1st element : ok
uneListe.reste().afficherElement(); // displaying 2nd element : pointer is NULL !!???
// uneListe.reste().reste().afficherElement(); --> segmentation fault, predictably enough
return 0;
}
As you can guess, it doesn't work. When I add elements, calling the display method within the add method, elements seem to be well formed although the pointer value and the next element's adress differ (I don't get why).
But, after adding process is done, I try to display the list again, 1st element is well linked with 2nd, but then there is a NULL pointer value. Wonder why ??
I've seen a code with two classes (Node and List), that works fine, but I'd like to know what is wrong with mine. Is it that I'm creating new objects of a class within this same class ?
Thanks,
for right this problem you most change this line
Liste suffixer(int val)
to
Liste* suffixer(int val)
and then change this line
return *suivante;
to
return suivante;
and in main use this line
uneListe.suffixer(5)->suffixer(8)->suffixer(10);
instead of
uneListe.suffixer(5).suffixer(8).suffixer(10);
Your class methods Liste::prefixer(int val) and Liste suffixer(int val) will return a copy of the object created, they should return a pointer to the object (or a reference).
e.g.
Liste *Liste::suffixer(int val){
if(suivante == nullptr)
suivante = new Liste(val);
else
throw std::runtime_error("Generic error message");
return suivante;
}
or
Liste &Liste::suffixer(int val){
... previous inner method ...
return *suivante;
}
Class Liste contains a value and a reference, which is not what a list is: a singly linked list is a pointer to an element containing a value and a pointer to the next node.
You might use a value+pointer element as a list object, ignoring the val member. This would require different coding for some methods, e.g., for tete() and reste().
But, since using
typedef Liste * real_list_type;
is what you have in mind (? - see below), let's look at the methods.
bool estVide() const { return (suivante == NULL); }
This is in contradiction to the real_list_type being a mere List *; if you compare this to method reste(), it actually tests whether the tail is empty, not the list itself! (It would be in sync with using a value+pointer object as the list object.)
Liste suffixer(int val) { suivante = new Liste(val); ... }
This is bad: it replaces suivante with a new object, no matter what's stored in there (a memory leak). You'll have to do
Liste suffixer(int val) {
if( suivante == NULL ){
suivante = new Liste(val);
} else {
suivante->suffixer( val );
}
return *this;
}
LATER
I think that would be the best way to keep it as close to the abstract concept as possible. Note that there is no "isEmpty" - this is done by a test whether the List * variable representing the list equals NULL, but you can't have a method for that.
template<typename T>
class List {
public:
List( T v, List* t = nullptr ) : value(v), next(t){}
~List(){ delete next; }
List* prepend( T v ){
return new List( v, this );
}
List* append( T v ){
if( next == nullptr ){
next = new List( v );
} else {
next->append( v );
}
return this;
}
T head(){ return value; }
List* tail(){ return next; }
void dump(){
List* curr = this;
std::string del = "";
while( curr != nullptr ){
std::cout << del << curr->value;
del = ", ";
curr = curr->next;
}
std::cout << std::endl;
}
private:
T value;
List* next;
};
int main(){
typedef List<int> * intList;
intList list = new List<int>( 1 );
list->append( 2 )->append( 3 );
list->dump();
}
Here is the "fixed" version of my first attempt :
#include <iostream>
using namespace std;
class Liste {
private :
int val;
bool vide;
Liste *suivante;
public :
Liste(int val = 0, bool vide = true, Liste *suivante = NULL) {
this->val = val;
this->vide = vide;
this->suivante = suivante;
}
void afficherElement() const {
cout << "Adresse : " << this << endl;
cout << "Valeur : " << val << endl;
cout << "Vide : " << vide << endl;
cout << "Adresse suivante : " << suivante << endl;
cout << endl;
}
int head() const {
return val;
}
Liste *reste() const {
return suivante;
}
bool estVide() const {
return vide;
}
Liste *prefixer(int val) {
Liste *nouvelle = new Liste(val, this);
return nouvelle;
}
Liste *suffixer(int val) {
if(suivante == NULL) {
suivante = new Liste(val);
vide = false;
}
return suivante;
}
};
void afficherListe(Liste *uneListe) {
(*uneListe).afficherElement();
if(!(*uneListe).estVide()) {
afficherListe((*uneListe).reste());
}
}
int main() {
Liste *test = new Liste(3);
(*test).suffixer(5);
afficherListe(test);
return 0;
}
As expected it's awfully unpractical.
Laune's solution looks good...
However, the whole thing is bizarre, I suppose I'd be better off sticking with the regular List/Nodes way. Definitely gonna talk about that with my teacher.