I try to write a bidirectional list. I must use overloaded operators([] and +=). [] - access to given node. += - add node to my list. I wrote these methods and it looks ok, but it doesn't work and I have no idea why.
here are core lines of my code:
//********************************************************
CLista::CNode* CLista::operator[](int i_index)
{
int i_help = 0;
CNode* c_result = &c_root;
while(i_help < i_index)
{
c_result = c_result->getNext();
i_help++;
}
return c_result;
}
//*********************************************************
void CLista::operator+=(void* pv_object)
{
if(i_numNodes == 0)
{
c_root = CNode(pv_object);
}
else
{
CNode c_new = CNode(pv_object);
CNode* c_help = this->operator[](i_numNodes - 1);
c_new.setPrevious(c_help);
(*c_help).setNext(&c_new);
}
i_numNodes++;
}
int _tmain(int argc, _TCHAR* argv[])
{
CLista list = CLista();
string s1 = string("first");
void* wsk1 = &s1;
string s2 = string("second");
void* wsk2 = &s2;
string s3 = string("third");
void* wsk3 = &s3;
list += wsk1;
list += wsk2;
list += wsk3;
void* res1 = (*list[0]).getObject();
void* res2 = (*list[1]).getObject();
void* res3 = (*list[2]).getObject();
cout << "res1: " << res1 << endl;
cout << "res2: " << res2 << endl;
cout << "res3: " << res3 << endl;
cout << "wsk1:" << wsk1 << endl;
cout << "wsk2:" << wsk2 << endl;
cout << "wsk3:" << wsk3 << endl;
}
and here is header:
class CLista
{
public:
class CNode
{
public:
CNode(void)
{
pc_next = NULL;
pc_previous = NULL;
}
CNode(void* pv_object)
{
pc_next = NULL;
pc_previous = NULL;
this->pv_object = pv_object;
}
CNode* getNext(){return pc_next;};
CNode* getPrevious(){return pc_previous;};
void* getObject(){return pv_object;};
void setNext(CNode* pc_next);
void setPrevious(CNode* pc_previous);
private:
CNode* pc_next;
CNode* pc_previous;
void* pv_object; // czy to jest dobrze?
};
CNode c_root;
int i_numNodes;
public:
CLista(void);
~CLista(void);
CNode* operator[](int index);
void operator+=(void* object);
};
When I add third element to list and then check it, it is strange problem: addresses of res2 and res3 are the same.
In your operator += function, you create a local CNode called c_new that you link into your linked list. When the scope ends (which happens before the function returns), that local is destructed, leaving the list dangling (pointing at a no longer valid CNode, whose memory is about to be reused for some other local variable, such as the next c_new created on the next call to the function).
Using/accessing an object after it has gone out of scope and been destroyed is undefined behavior, so will generally crash or otherwise misbehave.
Related
In a C++ program I define several data structures and the class DataClass.
Content of DataClass.h :
typedef struct
{
int ta;
long tb;
} DataTrgType;
typedef std::vector <DataTrgType> TRG_Type;
typedef struct
{
int a;
long b;
bool c;
TRG_Type* TRG;
} DataType;
class DataClass
{
private:
DataType* myData;
std::vector <DataType> myDatas;
DataTrgType* dataTRG;
public:
DataClass(std::string pLogFile);
virtual ~DataClass();
void createData();
void setA (int a);
void setB (long b);
void setC (bool c);
void addData();
void createDataTRG();
void setTA (int ta);
void setTB (long tb);
void addDataTRG ();
};
DataClass.cpp :
void DataClass::createData()
{
if (myData != NULL)
{
delete myData;
myData = NULL;
}
myData = new DataType;
}
void DataClass::addData()
{
if (myData != NULL)
{
myDatas.push_back(*myData);
delete myData;
myData = NULL;
}
}
void DataClass::createDataTRG()
{
if (dataTRG != NULL)
{
delete dataTRG;
dataTRG = NULL;
}
dataTRG = new DataTrgType;
}
void DataClass::addDataTRG ()
{
if (dataTRG != NULL)
{
(*(myData->TRG)).push_back(*dataTRG);
delete dataTRG;
dataTRG = NULL;
}
}
In main I run this code:
DataClass classObj;
classObj.createData();
classObj.setA (11);
classObj.setB (12);
classObj.setC(false);
classObj.createDataTRG();
classObj.setTA (110);
classObj.setTB (112);
classObj.adddataTRG ();
classObj.createDataTRG();
classObj.setTA (105);
classObj.setTB (107);
classObj.adddataTRG ();
classObj.addData();
classObj.createData();
classObj.setA (21);
classObj.setB (22);
classObj.setC(false);
classObj.createdataTRG();
classObj.setTA (210);
classObj.setTB (212);
classObj.adddataTRG ();
classObj.addData();
In the program I correctly display the content of these data structures with:
typename std::vector <DataType> :: iterator it;
typename std::vector <DataTrgType> :: iterator itTrg;
std::cout << "myDatas has " << myDatas.size() << " elements" << std::endl;
for (it = myDatas.begin(); it != myDatas.end(); ++it)
{
std::cout << "Data.a = " << (*it).a << " Data.b = " << (*it).b << std::endl;
for (itTrg = (*it).TRG->begin(); itTrg != (*it).TRG->end(); ++itTrg)
{
std::cout << " Trg.ta = " << (*itTrg).ta << " Trg.tb = " << (*itTrg).tb) << std::endl;
}
}
In the destructor of the class I want to release the memory.
I have tried with this code:
DataClass::~DataClass()
{
typename std::vector <DataType> :: iterator it;
typename std::vector <DataTrgType> :: iterator itTrg;
for (it = myDatas.begin(); it != myDatas.end(); ++it)
{
for (itTrg = (*it).TRG->begin(); itTrg != (*it).TRG->end(); ++itTrg)
{
delete (*itTrg);
}
delete it;
}
}
But it does not compile, showing numerous errors in the destructor.
I have done some other test, but it doesn't compile either and I can't find the correct code to free the memory.
for (int i=0; i<myDatas.size(); i++)
delete (myDatas[i]);
myDatas.clear();
Any help or suggestion is appreciated.
Consider the following example:
#include<iostream>
#include <vector>
using namespace std;
const int UN_INITIALIZED = -1;
struct Node
{
Node *rightNode = nullptr;
int data = UN_INITIALIZED;
};
struct Test {
Test()
{
root.data = 1;
}
void link_nodes()
{
Node n;
cout << "address : " << &n << endl;
n.data = 5;
root.rightNode = &n;
} // n is destroyed? n.rightNode shouldn't be defined?
Node root;
};
int main()
{
Test t;
t.link_nodes();
// out of scope?
cout << t.root.rightNode->data << endl; // This return -1
return 0;
}
When running the following example I get a value of -1 which is the default uninitialized value of Node::data. in link_nodes() shouldn't n be destroyed after the function call ends and calling ->data should give an error or seg fault?
Also when I try creating a different scope in the main function the results tend to be correct :
int main()
{
Node root;
{
Node n;
n.data = 55;
root.rightNode = &n;
} // n died?
cout << root.rightNode->data << endl; //gives 55
return 0;
}
The scope of the variable n is the block scope of the member function link_nodes.
void link_nodes()
{
Node n;
cout << "address : " << &n << endl;
n.data = 5;
root.rightNode = &n;
}
After exiting the function the variable n having automatic storage duration will not be alive. As a result the pointer root.rightNode will be invalid. Dereferencing the pointer results in undefined behavior.
The same problem exists in the second program
Node root;
{
Node n;
n.data = 55;
root.rightNode = &n;
} // n died?
cout << root.rightNode->data << endl;
Again the variable n is not alive after passing the control outside the compound statement where it is defined. So the pointer root.rightNode will be invalid.
Trying to create a Hash Table with name as key and it's value as the drink, when printTable() is invoked without adding items (i.e. commenting out the following code snippet) it prints out the added image without any address boundary error :
h.addItem("Paul", "Locha");
h.addItem("Kim", "Iced Mocha");
h.addItem("Emma", "Strawberry Smoothy");
h.addItem("Annie", "Hot Chocolate");
h.addItem("Sarah", "Passion Tea");
h.addItem("Pepper", "Caramel Mocha");
h.addItem("Mike", "Chai Tea");
h.addItem("Steve", "Apple Cider");
h.addItem("Bill", "Root Bear");
h.addItem("Marie", "Skinny Latte");
Image of output with given default values
But when items are added either by parsing csv or directly calling the class member function of addItem(), the program renders an error like so :
➜ Hash Table ./hashTableWithStruct.o
fish: Job 1, './hashTableWithStruct.o' terminated by signal SIGSEGV (Address boundary error)
The erroneous code is herewith :
hashTableWithStruct.cpp
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
class Hash
{
private:
static const int tableSize = 10;
struct item
{
string name;
string drink;
item *next;
};
item *hashTable[tableSize];
public:
Hash()
{
for (int i = 0; i < tableSize; i++)
{
hashTable[i] = new item;
hashTable[i]->name = "empty";
hashTable[i]->drink = "empty";
hashTable[i]->next = NULL;
}
}
int hashFunct(string key)
{
int hashed = 0;
for (int i = 0; key[i] != '\0'; i++)
{
hashed += (int)key[i];
}
return (hashed % tableSize); //returns the BUCKET value
}
void addItem(string name, string drink)
{
int BUCKET = hashFunct(name);
//if the value at BUCKET hasn't been written yet,
//override the default empty ones
if (hashTable[BUCKET]->name == "empty")
{
hashTable[BUCKET]->name = name;
hashTable[BUCKET]->drink = drink;
}
//otherwise, make a linked list starting from BUCKET
else
{
item *ptr = hashTable[BUCKET];
item *n = new item;
n->name = name;
n->drink = drink;
n->next = NULL;
//the linked list might contain many nodes,
//hence travel to last node and reassign the last node to be this new item
while (ptr != NULL)
ptr = ptr->next;
ptr->next = n;
}
return;
}
int numOfItemsInBucket(int BUCKET)
{
int count = 0;
if (hashTable[BUCKET]->name == "empty")
return count;
else
{
count++;
item *ptr = hashTable[BUCKET];
while (ptr->next != NULL)
{
count++;
ptr = ptr->next;
}
}
return count;
}
void printTable()
{
int num; //holds number of elements(items) in each bucket
for (int i = 0; i < tableSize; i++)
{
num = numOfItemsInBucket(i);
cout << "-----------------------------\n"
<< "Table[" << i << "]" << endl
<< hashTable[i]->name << endl
<< hashTable[i]->drink << endl
<< "# of items in bucket " << i << " : " << num << endl;
cout << "-----------------------------\n";
}
}
};
int main(int argc, char const *argv[])
{
Hash h;
/* string filename = "datasetForHashTable.csv";
ifstream myCSV(filename.c_str());
if (!myCSV.is_open())
{
cout<<"Failed to open file"<<endl;
return 0;
}
string name, drink;
while (myCSV.peek()!=EOF)
{
getline(myCSV, name, ',');
getline(myCSV, drink, '\n');
h.addItem(name, drink);
}
myCSV.close(); */
h.addItem("Paul", "Locha");
h.addItem("Kim", "Iced Mocha");
h.addItem("Emma", "Strawberry Smoothy");
h.addItem("Annie", "Hot Chocolate");
h.addItem("Sarah", "Passion Tea");
h.addItem("Pepper", "Caramel Mocha");
h.addItem("Mike", "Chai Tea");
h.addItem("Steve", "Apple Cider");
h.addItem("Bill", "Root Bear");
h.addItem("Marie", "Skinny Latte");
h.printTable();
return 0;
}
Basically, can't understand why the table won't print with name and drinks, since it works just fine when table is printed without it.
Please help, trying to figure this out since 2 days.
After this while loop
while (ptr != NULL)
ptr = ptr->next;
the pointer ptr is equal to nullptr. So the next statement
ptr->next = n;
invokes undefined behavior due to dereferencing a null pointer.
It seems you mean
while (ptr->next != NULL)
ptr = ptr->next;
ptr->next = n;
I am trying to create a for loop that runs through a vector, pushes something into a queue, updates all appropriate elements of the struct of the vector and then loops again until the vector is empty.
The problem I am having is that the function to run through my for loop does not seem to be updating my vector elements, I am getting the same output even though I know it should be changing.
Here is the link to a gist of the project https://gist.github.com/sgodfrey321/6cffd85896432b2942aa , it just has a lot of filler, I had to input alot of stuff by hand so it is kinda messy.
So to start with I pass my vector to the function in a while loop
void queueNodes(vector<vertex>& list, queue<vertex>& q);
as such
int counter = 0;
while (counter < 11) {
queueNodes(nodes, q);
counter++;
}
the while loop is to make sure that I run through the vector enough times. Now in the function I check to see if incomingEdges of the struct is 0 and if it is I push it into the queue and update the next door incomingEdges:
void queueNodes(vector<vertex>& nodes, queue<vertex>& q) {
for (auto i : nodes) {
cout << endl << i.vertexName << " ";
if (i.incomingEdges == 0) {
i.nextDoorTop->incomingEdges--;
i.nextDoorMiddle->incomingEdges--;
i.nextDoorBottom->incomingEdges--;
q.push(i);
cout << "foo";
} else {
cout << "bar";
}
}
Now I would expect that when I use the function again with the updated vector I would see a change in output as updating the next door incomingEdges would cause some to trigger the condition I am looking for. I have written outputs that show the next door incomingEdges are in fact decreasing however I can not seem to use the updated vector in the function call.
Do I need to return the vector somehow? I am sure that I am calling the function incorrectly, but does anyone have any ideas?
Thank You
edit: forgot to pass by reference the queue!
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct vertex {
char vertexName;
int incomingEdges;
vertex* nextDoorTop;
vertex* nextDoorMiddle;
vertex* nextDoorBottom;
};
void queueNodes(vector<vertex>& nodes);
int main() {
vertex s, A, G, D, B, H, E, C, I, F, t;
s.vertexName = 's';
s.incomingEdges = 0;
s.nextDoorTop = &A;
s.nextDoorMiddle = &D;
s.nextDoorBottom = &G;
A.vertexName = 'A';
A.incomingEdges = 2;
A.nextDoorTop = &B;
A.nextDoorMiddle = &E;
G.vertexName = 'G';
G.incomingEdges = 1;
G.nextDoorTop = &D;
G.nextDoorMiddle = &E;
G.nextDoorBottom = &H;
D.vertexName = 'D';
D.incomingEdges = 2;
D.nextDoorMiddle = &E;
B.vertexName = 'B';
B.incomingEdges = 1;
B.nextDoorTop = &C;
H.vertexName = 'H';
H.incomingEdges = 1;
H.nextDoorTop = &E;
H.nextDoorMiddle = &I;
E.vertexName = 'E';
E.incomingEdges = 4;
E.nextDoorTop = &C;
E.nextDoorMiddle = &F;
E.nextDoorBottom = &I;
C.vertexName = 'C';
C.incomingEdges = 3;
C.nextDoorMiddle = &t;
I.vertexName = 'I';
I.incomingEdges = 2;
I.nextDoorTop = &F;
I.nextDoorMiddle = &t;
F.vertexName = 'F';
F.incomingEdges = 2;
F.nextDoorMiddle = &t;
t.vertexName = 't';
t.incomingEdges = 3;
vector<vertex> nodes { s, A, G, D, B, H, E, C, I, F, t };
cout << "Vertex Name: " << " Number Of Edges: " << endl;
for (auto i : nodes) {
cout << i.vertexName << " " << i.incomingEdges << " "
<< endl;
}
int counter = 0;
while (counter < 11) {
queueNodes(nodes);
counter++;
}
return 0;
}
void queueNodes(vector<vertex>& nodes) {
for (auto& i : nodes) {
cout << endl << i.vertexName << " ";
if (i.incomingEdges == 0) {
i.nextDoorTop->incomingEdges--;
i.nextDoorMiddle->incomingEdges--;
i.nextDoorBottom->incomingEdges--;
cout << "foo";
} else {
cout << "bar";
}
}
}
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.