I am using friend classes and I want to use rec's destructor when I call DeleteItem from my list class. However, there is a pointer pointing to the record that I want to delete, so I do not know how to call the destructor so it will do what I want.
Classes:
class rec
{
friend class list;
private:
char * id;
char firstname[15];
char lastname[15];
int ar[10];
rec* prev;
rec* next;
public:
void SetData ( char * id_in, char * fn, char * ln, int * ans_in);
rec( char *i, char *fn, char *ln, int *a);
rec();
void operator= (const rec& r);
rec (const rec& r);
void Print();
~rec();
};
class list
{
private:
rec* first;
rec* last;
public:
int AddItem(rec r);
int DeleteItem (char* delid);
void PrintList( int order);
int ReadData(char *inanswer, char *inkey);
int WriteData(char *answer, char *key);
list();
void operator= (list l);
private:
int CheckDuplicate(rec r);
void DeleteData();
int Count(char *filename);
};
~rec()
rec :: ~rec()
{
if (id != NULL)
{
delete [] id;
}
}
DeleteItem (Snipet)
int list :: DeleteItem(char *delid)
{
int id_counter;
rec *current = first;
while (current || current == NULL)
{
if (current == NULL)
{
return 0;
}
else
{
id_counter = strcmp(current -> id, delid);
if (id_counter != 0)
{
current = current -> next;
}
else
{
if (current == first && current != last)
{
~rec(); //Here
first = current -> next;
delete current;
first -> prev = NULL;
return 1;
}
Delete item will compile fine if I manually put in delete[] current ->id; but does this when I try to compile as is
list.cpp:292: error: no match for ‘operator~’ in ‘~rec()’
/usr/lib/gcc/x86_64-redhat-linux/4.3.0/../../../../include/c++/4.3.0/bits/ios_base.h:105: note: candidates are: std::_Ios_Fmtflags std::operator~(std::_Ios_Fmtflags)
/usr/lib/gcc/x86_64-redhat-linux/4.3.0/../../../../include/c++/4.3.0/bits/ios_base.h:145: note: std::_Ios_Openmode std::operator~(std::_Ios_Openmode)
/usr/lib/gcc/x86_64-redhat-linux/4.3.0/../../../../include/c++/4.3.0/bits/ios_base.h:183: note: std::_Ios_Iostate std::operator~(std::_Ios_Iostate)
Is it my destructor that needs to be fixed or is it something to do in DeleteItem?
To call a destructor, the syntax is current->~rec(), but in almost cases, you shouldn't call directly a destructor in C++. By calling delete current, the C++ will automatically call the destructor before de-allocating its memory.
In your case, as you are also using delete after calling ~rec(), your destructor will be called twice, so you will result to a double free corruption as your constructor is trying to free id memory.
Advice: To avoid using friend class, in your case, next and prev members are related to the list, and not to rec class. So your list class could have a nested decorator class that will wrap your rec class like:
class list
{
struct item {
rec* value;
rec* prev;
rec* next;
};
item * first;
item * last;
public:
// ...
};
Then the logic of your list will be well isolated from the logic of your rec class.
Related
I am using a doubly linked list and I am trying to use the data that is passed by reference to insert a node before said data. I've used string* data = new string(s); to allocate memory however, when I try to use data I get an error.
#ifndef __DOUBLYLINKEDLIST_H__
#define __DOUBLYLINKEDLIST_H__
//
//
#include
#include
using namespace std;
class DoublyLinkedList {
public:
DoublyLinkedList();
~DoublyLinkedList();
void append (const string& s);
void insertBefore (const string& s);
void insertAfter (const string& s);
void remove (const string& s);
bool empty();
void begin();
void end();
bool next();
bool prev();
bool find(const string& s);
const std::string& getData() const;
private:
class Node
{
public:
Node();
Node(const string& data);
~Node();
Node* next;
Node* prev;
string* data;
};
Node* head;
Node* tail;
Node* current;
};
void DoublyLinkedList::insertBefore(const string& s)
{
Node* ptr = head;
string* data = new string(s);
if (head == NULL)
{
append(s);
return;
}
if (head == current)
{
//this is where I get an error...
this->data= new Node();
current->prev = head;
current = head;
return;
}
There is no reason to use a pointer to a string, which forces you to manage memory. Use a simple string instead.
But this is not the problem here. Here a local variable has the same name than a class member of a Node , and the member in node gets never initalized. Furtherthermore the DoublyLinkedList has itself no such member, so this->data is unknown. See my comments here:
void DoublyLinkedList::insertBefore(const string& s)
{
...
string* data = new string(s); // --> ok, but this is local variable
if (head == NULL)
{
append(s);
return; // ouch !!! memory leak !! data pointer is never freed
}
if (head == current)
{
//this is where I get an error...
this->data= new Node(); // --> 'this' is a DoublyLinkedList, not a Node
...
return;
}
Now this being said, is it possbile that you make a confusion between the DoublyLinkedList and the nodes it contains ? See here a start of correction, but you need to do more to handle the linking between the nodes:
void DoublyLinkedList::insertBefore(const string& s)
{
Node* ptr = head;
if (head == NULL)
{
append(s);
return;
}
if (head == current)
{
string* data = new string(s);
Node nd = new Node();
nd->data = data; // initialize node's pointer
nd->prev = ... // you need to link new node to the rest
nd->next = ...
... // and you need to update the previous and next node
return;
}
Now, as said in the first place, replace the pointer to string with a string. At least, you'll avoid leaking memory, shallow copies, and lots of other troubles. Then you can focus better on the real problems of a linked list data structure.
The code is really simple, consisting of nested similar method calls on different classes, but the program keeps segfaulting on random occasions (always during the Add method though).
I call method Add on class CScreen instance to add an object CCircle, which is cloned (a new instance is created) and pointer is passed down hierarchically until it reaches xnode (my own implementation of list node).
int main (){
CScreen S1;
S1 . Add ( CCircle ( 3, 20, 20, 15 ) );
}
class CScreen {
public:
Qlist <Planimetric*> objects;
void Add (const Planimetric & ob ) {
objects.push_back( ob.clone() ); // returns pointer to new instance
}
...
template<typename _Type>
class Qlist{
public:
Qnode <_Type> *start;
Qlist() : start(null) {
start = new Qnode<_Type>(-coordInfinity, -coordInfinity, coordInfinity, coordInfinity);
}
void push_back (const _Type & data) {
start->push_back(data);
}
...
template<typename _Type>
struct Qnode{
xlist <_Type> objects;
void push_back (const _Type & data) {
objects.push_back(data);
}
...
template<typename _Type>
class xlist{
public:
int sizeOfList;
xnode<_Type> *first, *last;
void push_back (const _Type & data) {
sizeOfList ++;
xnode<_Type> *nnp; // new node pointer ;)
if(first == null)
first = last = new xnode<_Type> (data);
else
last = last->next = nnp = new xnode<_Type>(data, last);
}
...
template<typename _Type>
struct xnode{
_Type data;
xnode *prev, *next;
xnode(const _Type & data, xnode* prev = null, xnode* next = null)
: data(data), prev(prev), next(next) {}
...
class CCircle : public Planimetric {
public:
long long int x,y;
long long int rsq;
CCircle * clone () const {return new CCircle(*this);}
CCircle (int ID, int x, int y, int r) : Planimetric(ID), x(x), y(y) { ...
...
}
I thought it might be a stack overflow because of the high overhead, but it sometimes segfaults on first call. The xlist and xnode implementation worked perfectly until i implemented Qlist.
I'm passing down const pointer reference, which is then copied in constructor in xnode. Could the problem be there? I've tried to debug with gdb, with no luck.
I have a misunderstanding here. I'm designing a Queue class which uses the Client class as the base unit. The list structure consists of a pointer to the next data and a variable for holding the Client object.
The Queue class has 2 ways of operating. The way of it's operating is determined by the bool MODE variable. If mode equals 1, then the Queue class uses placement new operator, and if not, it uses the new operator.
This is the Queue class's prototype:
class Queue
{
private:
const int max_lists;
int no_lists, counter;
char client_value;
list *chunk;
list *top;
const bool MODE;
void addFirst(const Client &c);
void addLast(const Client &c);
void deleteLast();
void deleteFirst();
void clean_mem();
void clean_mem(list *&pos);
list* malloc();//T* type
public:
Queue();
Queue(list *buffer,int no);
Queue(const Queue &q);
Queue& operator=(const Queue &q);
~Queue() { clean_mem(); }
void enqueue(const Client &c);
void timeoutCustomers();
void decreaseTimeout();
Client getCustomer() const;
void finishCustomer();
void show() const;
};
The functions definitions which contribute to the error given are here:
void Queue::addFirst(const Client &c)
{
if(top==nullptr)
{
top = malloc();
top->info = c;
top->next = nullptr;
}
else
{
list *add = malloc();
add->info = c;
add->next = top;
top = add;
}
}
list* Queue::malloc()
{
if(MODE)
{
if(no_lists==max_lists)
{
return nullptr;
}
else
{
list *tmp = chunk;
counter = 0;
while(counter++<max_lists)
{
client_value = (char)tmp->info;
if(client_value==-1 && tmp->next==nullptr)
{
return new(tmp) list;
}
tmp++;
}
return nullptr;
}
}
else
{
return new list;
}
}
And here's the list structure:
struct list { Client info; list *next; };
When I make an instance of the Queue class, I can choose whether I go on placement new or just the new operator.
If I choose the placement new, I'm have to send the address of an array of type list. The address is saved into chunk pointer. The top pointer holds the first address of the linked list.
Then, if I call the addFirst() function it stops at top->info = c. The error points to top->info :
CXX0030: Error: Expression cannot be evaluated.
But when I switch back to new operator, everything works. This tells me that there's a problem with the allocation of a new portion in the already allocated memory.
Can somebody give me a direction of what's the problem here?
Did you intend your malloc function like that? You have an 'else' block AFTER your functions definition and not all control paths return a value. I have a rewrite here that seems to match more with what I think you actually intend:
list* Queue::malloc()
{
if(MODE)
{
if(no_lists==max_lists)
{
return nullptr;
}
else
{
list *tmp = chunk;
counter = 0;
while(counter++<max_lists)
{
client_value = (char)tmp->info;
if(client_value==-1 && tmp->next==nullptr)
{
return new(tmp) list;
}
tmp++;
}
return nullptr;
}
}
else
{
return new list;
}
}
I want to independently overload operator+ to concatenate 2 double chained lists. My idea is to obtain the address of the first element from the first list and the address of the first element from the second list.
In DoubleChainedList class, except the constructor, destructor and the next 4 methods which are working fine, I made a method called get_prim which is supposed to get me the address of the first element from the specified list. Then, using the method get_current I want to move through the first list until it ends while adding in the third list the elements, and then apply the same principle to the second list.
But I have a problem, I get
'get_prim' was not declared in this scope
and
'get_current' was not declared in this scope
at the bolded tagged lines (see the code below) when I compile. What am I missing?
#include <iostream>
#include <stdlib.h>
using namespace std;
//Create node class
class Node
{
private:
float value;
Node *back;
Node *next;
public:
Node *set_value (Node *x, float element) { x->value=element; return x; }
float get_value (Node *x) { return x->value; }
Node *set_back (Node *x) { return x->back; }
Node *set_next (Node *x) { return x->next; }
Node *set_back_nod (Node *x, Node *y) { x->back=y; return x; }
Node *set_next_nod (Node *x, Node *y) { x->next=y; return x; }
void next_to_2next (Node *x) { x->next=x->next->next; }
void next_back_to_origins (Node *x) { x->next->back=x; }
};
//Create list class
class DoubleChainedList : public Node
{
private:
Node *prim;
Node *ultim;
public:
DoubleChainedList() { prim=NULL; ultim=prim; } //Constructor
~DoubleChainedList(); //Destructor
void insert_back(float element); //Inserts an element at the end of the list
void delete_element_from_position(int delete_position); //Deletes from the list the element whose position is equal to "delete_position"
void show_left_right(); //Shows the list from the first element to the last one
void show_right_left(); //Shows the list from the last element to the first one
Nod *get_prim (DoubleChainedList myList) { return this->prim; }; //Intended to obtain the address of the first element from "myList"
Nod *get_current (Node *x) { return set_next(x); }; //Intended to move me through the list
};
DoubleChainedList operator+ (DoubleChainedList myList1, DoubleChainedList myList2)
{
DoubleChainedList myList3;
Nod *current1,*current2;
current1=get_prim(myList1); // ERROR OVER HERE!
current2=get_prim(myList2);
cout<<get_value(current1)<<" "; // ERROR OVER HERE!
cout<<get_value(current2)<<" ";
return myList3;
}
int main()
{
int i,number_elem_myList1,number_elem_myList2,element;
DoubleChainedList myList1,myList2,myList3;
cin>>number_elem_myList1;
for (i=0;i<number_elem_myList1;i++)
{
cin>>element;
myList1.insert_back(element);
}
cin>>number_elem_myList2;
for (i=0;i<number_elem_myList2;i++)
{
cin>>element;
myList2.insert_back(element);
}
myList3=myList1+myList2;
return 0;
}
If you implement operator+= as a member function, you can have access to the other list's variables.
I would implement operator+=, then traverse the other list, appending the other list's nodes to this list.
And pass the other list as const &.
Functions called with e.g. someObject.function(...) or someObject->function(...) (where someObject is respectively either an object or a pointer to an object of some class that has a function(...) function) have direct access to the members of someObject.
So functions that are members of a class doesn't need to be passed an object of that class as parameter, unless you want to work with 2 objects in that function.
The functions of Node should probably look more like:
void set_value (float element) { value = element; }
float get_value () { return value; }
Node *set_back () { return back; }
Node *set_next () { return next; }
void set_back_nod (Node *y) { back = y; }
void set_next_nod (Node *y) { next = y; }
void next_to_2next () { next = next->next; }
void next_back_to_origins () { next->back = this; }
Also, get_prim:
Node *get_prim() { return prim; };
Which then leads to a operator+ which looks more like: (const & as Thomas suggested)
DoubleChainedList operator+ (const DoubleChainedList &myList1,
const DoubleChainedList &myList2)
{
DoubleChainedList myList3;
Node *current1, *current2;
current1 = myList1.get_prim();
current2 = myList2.get_prim();
cout << current1->get_value() << " ";
cout << current2->get_value() << " ";
// ...
return myList3;
}
Using operator+= as Thomas suggested is probably also a better idea.
Modified the program as follows, but I still have a problem. If I try to display the list outside the procedure "operator+", I get "Segmentation fault" (when I debugged the program it happened immediately after this instruction: "myList3=myList1+myList2;"). If I display it inside that procedure, everything's OK. I think that this is happening because of that "return" statement and because I return a temporary object which will no longer exists after the "operator+" procedure ends, but I don't know how to fix that.
#include <iostream>
using namespace std;
//Create node class
class Node
{
private:
float value;
Node *back;
Node *next;
public:
Node *set_value (float element) { value=element; return this; }
float get_value () { return value; }
Node *set_back () { return back; }
Node *set_next () { return next; }
Nod *set_back_node (Node *y) { back=y; return this; }
Nod *set_next_node (Node *y) { next=y; return this; }
void next_to_2next () { next=next->next; }
void next_back_to_origins () { next->back=this; }
};
//Create list class
class DoubleChainedList : public Node
{
private:
Node *prim;
Node *ultim;
public:
DoubleChainedList() { prim=NULL; ultim=prim; } //Constructor
~DoubleChainedList(); //Destructor
void insert_back(float element); //Inserts an element at the end of the list
void delete_element_from_position(int delete_position); //Deletes from the list the element whose position is equal to "delete_position"
void show_left_right(); //Shows the list from the first element to the last one
void show_right_left(); //Shows the list from the last element to the first one
Node *get_prim () { return prim; } //Intended to obtain the address of the first element from a list
};
DoubleChainedList operator+ (DoubleChainedList myList1, DoubleChainedList myList2)
{
DoubleChainedList myList3;
Node *current1,*current2,*current3;
current1=myList1.get_prim();
current2=myList2.get_prim();
while ((current1!=NULL)||(current2!=NULL))
{
if (current1!=NULL)
{
myList3.insert_back(current1->get_value());
current1=current1->set_next();
}
else
if (current2!=NULL)
{
myList3.insert_back(current2->get_value());
current2=current2->set_next();
}
}
//myList3.show_left_right();
//cout<<endl;
//myList3.show_right_left();
return myList3;
}
int main()
{
int i,number_elem_myList1,number_elem_myList2,element;
DoubleChainedList myList1,myList2,myList3;
cin>>nr_elem_lista1;
for (i=0;i<number_elem_myList1;i++)
{
cin>>element;
myList1.insert_back(element);
}
cin>>number_elem_myList2;
for (i=0;i<number_elem_myList2;i++)
{
cin>>element;
myList2.insert_back(element);
}
myList3=myList1+myList2;
myList3.show_left_right();
myList3.show_right_left();
return 0;
}
#Dukeling modified the methods of the "Node" class as you said (and of course the program where necessary) and it's OK, too.
I can post the full code if is someone interested, but it has more than 200 lines, the name of variables/dates, procedures/methods and also some comments wrote in Romanian (my natural language) and it would be harder for someone new to understand it.
I'm trying to create a doubly-linked list with the null object model. So far, I've implemented a method to add a node to the beginning of the list and a method to display the node. My problem is that the display function always displays 0. Can anyone point out where I've gone wrong and how to fix it? Also, am I on the right track to correctly implementing the null object model here?
Note: This is a school assignment. Please don't just post a solution without an explanation. I want to learn and understand what's going on here.
Edit: After fixing the display problem, I have another: When calling getHead() or getTail() with a list that is empty or has nodes, it keeps wanting to use self() from the node class, rather than the nullNode class (in the event of an empty list) or elementNode class (in the event of a list with nodes). I'm stuck on how to fix this.
If I print out the addresses of container.getNext() and container (for an empty list), both addresses are the same so shouldn't adding ->self() to the end call the self() method from the nullNode class?
class node {
public:
node(){/* Do nothing */}
node(int e){ element = e; }
int getData(){ return element; }
void setData(int e){ element = e; }
friend class list;
protected:
node* getNext(){ return next; }
void setNext(node* n){ next = n; }
node* getPrev() { return prev; }
void setPrev(node* n){ prev = n; }
node* self();
private:
int element;
node* next;
node* prev;
};
class nullNode : public node{
public:
nullNode(){/* Do nothing */}
int getData(){ return NULL; }
void setData(int e){ /* Do Nothing */ }
node* getNext(){ return head; }
void setNext(node* n){ head = n; }
node* getPrev() { return tail; }
void setPrev(node* n){ tail = n; }
node* self(){ return NULL; }
private:
node* head;
node* tail;
};
class elementNode : public node{
public:
elementNode(){/* Do nothing */}
elementNode(int element){
setData(element);
}
int getData(){ return node::getData(); }
void setData(int e){ node::setData(e); }
node* getNext(){ return node::getNext(); }
void setNext(node* n){ node::setNext(n); }
node* getPrev() { return node::getPrev(); }
void setPrev(node* n){ node::setPrev(n); }
node* self(){ return this; }
};
class list{
public:
list();
node* getHead(){ return (container.getNext())->self(); }
node* getTail(){ return (container.getPrev())->self(); }
node* addHeadNode(int e);
void removeNode(node* n);
void insertBefore(node* n, int e);
void insertAfter(node* n, int e);
void displayNode(node *n);
private:
nullNode container;
};
list::list()
{
container.setNext(&container);
container.setPrev(&container);
}
node* list::addHeadNode(int e)
{
node* foo = new elementNode(e);
foo->setPrev(&container);
foo->setNext(container.getNext());
container.getNext()->setPrev(foo);
container.setNext(foo);
return foo;
}
void list::displayNode(node* n)
{
cout << "Node Data: " << n->getData() << endl;
}
int main()
{
list myList;
node* myNode;
myNode = myList.addHeadNode(5);
myList.displayNode(myNode);
return 0;
}
elementNode(int element)
{
node e;
e.setData(element);
}
What is this code doing? You create node e, but it appears to then be thrown away and not added to any list.
The problem hides in
elementNode(int element){
node e;
e.setData(element);
}
What is going on here? First you create an instance of the node class and then call its setData member function. Sure enough e is modified with the value of element but the very next moment both e and element are vanished out of existence because the scope where they were initialized has ceased to its end (terminated by }) while the information in element hasn't been saved anywhere.
However, if you replace the above code with
elementNode(int element){
setData(element);
}
it calls the inherited setData member function, the value of element is saved, and the program outputs 5 as expected.
Your elementNode constructor is trying to initialize it's node part:
elementNode(int element){
node e;
e.setData(element);
}
You actually just construct an unrelated node then discard it.
What you want is to call your superclass constructor, which can be done in the subclass constructor's initialization list:
elementNode(int element) : node(element) {
}