I wrote this function in C++ as part of a bigger program:
Object Single_list<Object>::pop_front() {
//Single_node<Object> *tmp_front;
//Object hold;
if (empty()) {
throw underflow();
}
Single_node<Object> first_node = front();
Single_node<Object> *ptr = list_head;
list_head = list_head->next();
delete ptr;
return first_node.retrieve();
}
However, when I try and use this function I get the following message:
WARNING: calling delete twice on the same memory location: 0x100100160
I am really confused, I am not deleting the pointer (assuming that's what is causing the problem?) twice.
Any advice will be appreciated.
As requested, here is the constructor and retrieve function for the Single_Node, although I am not sure how helpful this will be to the error regarding deletion that I am getting:
template <typename Object>
Single_node<Object>::Single_node( const Object &e, Single_node<Object> *n ):element( e ), next_node( n )
{
// empty constructor
}
template <typename Object>
Object Single_node<Object>::retrieve() const
{
return element;
}
template <typename Object>
Single_node<Object> *Single_node<Object>::next() const
{
return next_node;
}
Building on Mankarse's comment, try the following:
Object Single_list<Object>::pop_front() {
if (empty()) {
throw underflow();
}
Object result = front().retrieve();
Single_node<Object> *ptr = list_head;
list_head = list_head->next();
delete ptr;
return result;
}
This grabs the result from the first node before it gets deleted.
Related
I'm trying to implement a class of Circular List with a nested class of iterator and I wrote like this:
template <class T>
class CircularList {
struct Item {
T data;
Item* next;
};
Item* head;
int size;
public:
CircularList() {
head = new Item();
head->next = head;
}
int sizeList() { return size; }
void push(T data) {
Item* i = new Item();
i->data = data;
i->next = head->next;
head->next = i;
size++;
}
class CircularListIterator {
Item* p;
CircularListIterator() {
p = head->next;
}
bool hasNext() {
if(p->next != head) {
return true;
}
return false;
}
T next() {
T data_temp = p->data;
p = p->next;
return data_temp;
}
};
CircularListIterator* iterator() {
return new CircularListIterator();
}
};
int main() {
CircularList<string>* letters = new CircularList<string>;
letters->push("d");
letters->push("c");
letters->push("b");
letters->push("a");
Iterator<string>* it= new Iterator<string>;
it = letters->iterator();
while (it->hasNext()) {
cout<< it->next() << "," << endl;
}
return 0;
}
But the Iterator is not working when I try to create an iterator in the main function, It said that it wasn't declared in the scope and has no member of it.
Assuming by "in the main class" you mean in the main function, the problem is quite straightforward: you're trying to construct a ::Iterator<string>, but there is no class in the global namespace (or anywhere else, in this code sample) called Iterator! You could try constructing a CircularList<string>::CircularListIterator - that's at least a class that exists - but it wouldn't work because the iterator needs to be associated with a CircularList object for it to be able to access member variables like head.
The correct thing to do here is to promote the iterator function - the one that returns a CircularListIterator* - out of the CircularListIterator class and into the CircularList class. Then, in your main function, you can call letters->iterator() and it'll return a CircularListIterator* for the letters object.
Now, CircularListIterator doesn't inherit from any other iterator classes - neither the (nonexistent-in-this-code Iterator you've typed it as, nor the C++ std::iterator or any of its variants) - so you can't assign it to it or probably even compile the code that references Iterator. To make CircularListIterator a subclass of std::iterator, you'll need to extend std::iterator<Category, T> with the appropriate category. See https://www.cplusplus.com/reference/iterator/iterator/ for more information on the std::iterator class template, including an example of implementing it.
I am very new to Microsoft visual studio C++ as well as std::unique_ptr. On CodeReview I was recommended to rewrite using std::unique_ptr. You can find the question I am referencing here.
Here are the following errors I am receiving:
1>main.cpp
1>c:\dev\linkedlist\linkedlist\singlelinkedlist.h(26): error C2760: syntax error: unexpected token 'identifier', expected ';'
1>c:\dev\linkedlist\linkedlist\singlelinkedlist.h(61): note: see reference to class template instantiation 'SingleLinkedList<T>' being compiled
1>c:\dev\linkedlist\linkedlist\singlelinkedlist.h(65): error C2760: syntax error: unexpected token 'identifier', expected ';'
1>c:\dev\linkedlist\linkedlist\singlelinkedlist.h(104): error C2760: syntax error: unexpected token 'identifier', expected ';'
1>c:\dev\linkedlist\linkedlist\singlelinkedlist.h(104): error C7510: 'make_unique': use of dependent type name must be prefixed with 'typename'
1>c:\dev\linkedlist\linkedlist\singlelinkedlist.h(120): error C2760: syntax error: unexpected token 'identifier', expected ';'
1>c:\dev\linkedlist\linkedlist\singlelinkedlist.h(120): error C7510: 'make_unique': use of dependent type name must be prefixed with 'typename'
1>c:\dev\linkedlist\linkedlist\singlelinkedlist.h(136): error C2760: syntax error: unexpected token 'identifier', expected ';'
1>c:\dev\linkedlist\linkedlist\singlelinkedlist.h(136): error C7510: 'make_unique': use of dependent type name must be prefixed with 'typename'
1>c:\dev\linkedlist\linkedlist\singlelinkedlist.h(145): error C2760: syntax error: unexpected token 'identifier', expected ';'
1>c:\dev\linkedlist\linkedlist\singlelinkedlist.h(145): error C7510: 'make_unique': use of dependent type name must be prefixed with 'typename'
1>c:\dev\linkedlist\linkedlist\singlelinkedlist.h(152): error C2760: syntax error: unexpected token 'identifier', expected ';'
1>c:\dev\linkedlist\linkedlist\singlelinkedlist.h(152): error C7510: 'make_unique': use of dependent type name must be prefixed with 'typename'
1>c:\dev\linkedlist\linkedlist\singlelinkedlist.h(164): error C2760: syntax error: unexpected token 'identifier', expected ';'
1>c:\dev\linkedlist\linkedlist\singlelinkedlist.h(164): error C7510: 'make_unique': use of dependent type name must be prefixed with 'typename'
1>Done building project "LinkedList.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Here is the header file:
#ifndef SingleLinkedList_h
#define SingleLinkedList_h
#include <iostream>
template <class T>
class SingleLinkedList {
private:
struct Node {
T data;
std::unique_ptr<Node> next = nullptr;
Node(T x) : data(x), next(nullptr) {}
};
std::unique_ptr<Node> head = nullptr;
std::unique_ptr<Node> tail = nullptr;
// This function is for the overloaded operator <<
void display(std::ostream &str) const {
for (std::make_unique<Node> loop = head; loop != nullptr; loop = loop->next) {
str << loop->data << "\t";
}
str << "\n";
}
public:
// Constructors
SingleLinkedList() = default; // empty constructor
SingleLinkedList(SingleLinkedList const &source); // copy constructor
// Rule of 5
SingleLinkedList(SingleLinkedList &&move) noexcept; // move constructor
SingleLinkedList& operator=(SingleLinkedList &&move) noexcept; // move assignment operator
~SingleLinkedList();
// Overload operators
SingleLinkedList& operator=(SingleLinkedList const &rhs);
friend std::ostream& operator<<(std::ostream &str, SingleLinkedList &data) {
data.display(str);
return str;
}
// Memeber functions
void swap(SingleLinkedList &other) noexcept;
void push(const T &theData);
void push(T &&theData);
void display() const;
void insertHead(const T &theData);
void insertTail(const T &theData);
void insertPosition(int pos, const T &theData);
void deleteHead();
void deleteTail();
void deletePosition(int pos);
bool search(const T &x);
};
template <class T>
SingleLinkedList<T>::SingleLinkedList(SingleLinkedList<T> const &source) {
for(std::make_unique<Node> loop = source->head; loop != nullptr; loop = loop->next) {
push(loop->data);
}
}
template <class T>
SingleLinkedList<T>::SingleLinkedList(SingleLinkedList<T>&& move) noexcept {
move.swap(*this);
}
template <class T>
SingleLinkedList<T>& SingleLinkedList<T>::operator=(SingleLinkedList<T> &&move) noexcept {
move.swap(*this);
return *this;
}
template <class T>
SingleLinkedList<T>::~SingleLinkedList() {
while (head != nullptr) {
deleteHead();
}
}
template <class T>
SingleLinkedList<T>& SingleLinkedList<T>::operator=(SingleLinkedList const &rhs) {
SingleLinkedList copy{ rhs };
swap(copy);
return *this;
}
template <class T>
void SingleLinkedList<T>::swap(SingleLinkedList &other) noexcept {
using std::swap;
swap(head, other.head);
swap(tail, other.tail);
}
template <class T>
void SingleLinkedList<T>::push(const T &theData) {
std::make_unique<Node> newNode = Node(theData);
if (head == nullptr) {
head = newNode;
tail = newNode;
newNode = nullptr;
}
else {
tail->next = newNode;
tail = newNode;
}
}
template <class T>
void SingleLinkedList<T>::push(T &&theData) {
std::make_unique<Node> newNode = Node(std::move(theData));
if (head == nullptr) {
head = newNode;
tail = newNode;
newNode = nullptr;
}
else {
tail->next = newNode;
tail = newNode;
}
}
template <class T>
void SingleLinkedList<T>::display() const {
std::make_unique<Node> newNode = head;
while (newNode != nullptr) {
std::cout << newNode->data << "\t";
newNode = newNode->next;
}
}
template <class T>
void SingleLinkedList<T>::insertHead(const T &theData) {
std::make_unique<Node> newNode = Node(theData);
newNode->next = head;
head = newNode;
}
template <class T>
void SingleLinkedList<T>::insertTail(const T &theData) {
std::make_unique<Node> newNode = Node(theData);
tail->next = newNode;
tail = newNode;
}
template <class T>
void SingleLinkedList<T>::insertPosition(int pos, const T &theData) {
}
template <class T>
void SingleLinkedList<T>::deleteHead() {
std::make_unique<Node> old = head;
head = head->next;
delete old;
}
template <class T>
void SingleLinkedList<T>::deleteTail() {
}
template <class T>
void SingleLinkedList<T>::deletePosition(int pos) {
}
template <class T>
bool SingleLinkedList<T>::search(const T &x) {
}
#endif /* SingleLinkedList_h*/
Here is the main.cpp file:
#include <algorithm>
#include <cassert>
#include <iostream>
#include <ostream>
#include <iosfwd>
#include "SingleLinkedList.h"
int main(int argc, const char * argv[]) {
///////////////////////////////////////////////////////////////////////
///////////////////////////// Single Linked List //////////////////////
///////////////////////////////////////////////////////////////////////
SingleLinkedList<int> obj;
obj.push(2);
obj.push(4);
obj.push(6);
obj.push(8);
obj.push(10);
std::cout<<"\n--------------------------------------------------\n";
std::cout<<"---------------displaying all nodes---------------";
std::cout<<"\n--------------------------------------------------\n";
std::cout << obj << std::endl;
//
// std::cout<<"\n--------------------------------------------------\n";
// std::cout<<"-----------------Inserting At End-----------------";
// std::cout<<"\n--------------------------------------------------\n";
// obj.insertTail(20);
// std::cout << obj << std::endl;
//
// std::cout<<"\n--------------------------------------------------\n";
// std::cout<<"----------------Inserting At Start----------------";
// std::cout<<"\n--------------------------------------------------\n";
// obj.insertHead(50);
// std::cout << obj << std::endl;
//
// std::cout<<"\n--------------------------------------------------\n";
// std::cout<<"-------------Inserting At Particular--------------";
// std::cout<<"\n--------------------------------------------------\n";
// obj.insertPosition(5,60);
// std::cout << obj << std::endl;
//
// std::cout<<"\n--------------------------------------------------\n";
// std::cout<<"----------------Deleting At Start-----------------";
// std::cout<<"\n--------------------------------------------------\n";
// obj.deleteHead();
// std::cout << obj << std::endl;
//
// std::cout<<"\n--------------------------------------------------\n";
// std::cout<<"----------------Deleting At End-----------------";
// std::cout<<"\n--------------------------------------------------\n";
// obj.deleteTail();
// std::cout << obj << std::endl;
//
//
// std::cout<<"\n--------------------------------------------------\n";
// std::cout<<"--------------Deleting At Particular--------------";
// std::cout<<"\n--------------------------------------------------\n";
// obj.deletePosition(4);
// std::cout << obj << std::endl;
// std::cout << std::endl;
//
// obj.search(8) ? printf("Yes"):printf("No");
std::cin.get();
}
I assume most of the errors are syntax errors or that I just made very careless mistakes. Thanks.
The bulk of your syntax errors are derivatives of the same mistake, so lets look at the first one. It's extra-instructive because it points out TWO mistakes. Any time you can kill two stones with one bird, I say go for it. It's damn hard to kill a stone at the best of times.
void display(std::ostream &str) const {
for (std::make_unique<Node> loop = head; loop != nullptr; loop = loop->next) {
str << loop->data << "\t";
}
str << "\n";
}
In std::make_unique<Node> loop = head, std::make_uniqueis a function that gives you a std::unique_ptr. It is not a type you can use to declare a variable. This mistake is repeated several times through the code and cleaning up all of them will reveal a bunch of new errors. Yay fun.
Step one, let's replace the function with the correct type.
void display(std::ostream &str) const {
for (std::unique_ptr<Node> loop = head; loop != nullptr; loop = loop->next) {
str << loop->data << "\t";
}
str << "\n";
}
Groovy. Simple. Unfortunately it doesn't work. std::unique_ptr<Node> loop = head means you now have two unique_ptrs to the same object. That doesn't sound particularly unique and the compiler won't allow it. You'll probably get some arcane error message about a deleted function. That's because unique_ptr's copy constructor and = operator have been banished to the dark dimension to make it harder to accidentally wind up with multiple unique_ptrs.
A quick note on why all this matters: Ownership. One of the biggest problems with pointers is deciding who deletes what and when. To sort this out you establish ownership of a pointer. Perhaps the pointer is to an Automatic variable and the stack or whatever's being used to manage Automatic data will take care of it. You delete NOTHING! Muhuhahahaha! Maybe someone newed the variable. Someone has to delete it. That someone is the owner. Some insane fool may even have malloced the object. Someone has to free it instead of deleteing it. Again, that's the owner.
There is no practical way to determine how the object was allocated and how it needs to be disposed of from a pointer. All a pointer does is point. The programmer needs to establish a contract that outlines who, if anybody, looks after it and how. In the old days this could be a painstaking process.
History has shown that contract-based pointer management is difficult to get right. And if you do get it right, the next programmer to come along can completely smurf it up. Pointer management works a lot better when the contract has some sort of automated enforcement.
Today we use a smart pointer. There are a bunch of smart pointers. We're sticking to unique_ptr this time. If you want to know more, read What is a smart pointer and when should I use one?
unique_ptr is the owner. It is a plain old Automatic variable that happens to contain a pointer that it will release as soon as the unique_ptr goes out of scope. No more worries about ownership. The owner is explicit. And like the Highlander, There can be only one.. If you have two unique_ptrs to the same object, it's not all that unique, is it? With two owners, one would go out of scope first and destroy the object, leaving the other owner a ticking timebomb.
Do not new a unique_ptr. That completely defeats the point.
You cannot copy a unique_ptr, but you can transfer ownership with std::move. Note that the object, if any, currently owned by the the destination unique_ptr will be destroyed.
If you call a function that receives a unique_ptr, you will have to transfer ownership to the receiving function parameter. The pointer will now be destroyed the function returns, assuming it doesn't transfer ownership of the pointer elsewhere. Since you often don't want this to happen, but still want the ownership of the pointer to remain clear, pass the unique_ptr by reference.
You can also use the unique_ptr::get method to get a raw, un-owned pointer, but whoever you give it to must know to leave the pointer alone, and that brings us back to contract- based pointer management.
Huh. That wasn't all that <expletive deleted> quick, was it? Sorry about that.
Getting back on topic, your pointer is owned by a unique_ptr. You can't just make a new copy with std::unique_ptr<Node> loop = head;, Highlander's law, above. Transferring ownership to a tightly scoped Automatic variable that will die and destroy the pointer at the end of the loop with std::unique_ptr<Node> loop = std::move(head); would leave the head pointer pointing to nothing. Not useful. The linked list is ruined, but at least everything in it was "correctly" destroyed.
As discussed above you can use a reference or get. You cannot reseat the reference with loop = loop->next, so reference cannot work. That leaves get
void display(std::ostream &str) const {
for (Node* loop = head.get(); loop != nullptr; loop = loop->next.get()) {
str << loop->data << "\t";
}
str << "\n";
}
There is very little room in which the programmer can get confused by the raw pointer here and ownership is still guaranteed by the unique_ptr, so all should be good.
This problem is repeated in
template <class T>
SingleLinkedList<T>::SingleLinkedList(SingleLinkedList<T> const &source) {
for(std::make_unique<Node> loop = source->head; loop != nullptr; loop = loop->next) {
push(loop->data);
}
}
Same solution.
There is a variant in the two push methods
template <class T>
void SingleLinkedList<T>::push(const T &theData) {
std::make_unique<Node> newNode = Node(theData);
if (head == nullptr) {
head = newNode;
tail = newNode;
newNode = nullptr;
}
else {
tail->next = newNode;
tail = newNode;
}
}
Solution (sort of):
template <class T>
void SingleLinkedList<T>::push(const T &theData) {
std::unique_ptr<Node> newNode = std::make_unique<Node>(theData);
if (head == nullptr) {
head = newNode;
tail = newNode;
newNode = nullptr;
}
else {
tail->next = newNode;
tail = newNode;
}
}
But note the assignments to head and tail. That violates Highlander and needs to be fixed. You can transfer ownership of newNode into head, but then there is nothing left to give tail. And why should tail get ownership of anything? Whatever tail points at should be owned by head or some Node's next. It's ownership is guaranteed. Since it's confined within SingleLinkedList it is entering into a contract with itself and can get away with just being a plain old pointer.
That means
Node* tail = nullptr;
and
template <class T>
void SingleLinkedList<T>::push(const T &theData) {
std::unique_ptr<Node> newNode = std::make_unique<Node>(theData);
if (head == nullptr) {
head = std::move(newNode);
tail = head.get();
}
else {
tail->next = std::move(newNode);
tail = tail->next.get();
}
}
I'm out of time, but
void SingleLinkedList<T>::insertHead(const T &theData)
and
void SingleLinkedList<T>::insertTail(const T &theData) {
need to be updated to the new world order and take into account "What if the list is empty?"
I'm reading lines from a file and storing them into linked list.
void add(llist *list, somevalue) {
Node *newnode = (Node *) malloc(sizeof(Node));
newnode->value = somevalue;
newnode->next = list->head;
list->head = newnode;
}
and I call this function from an initialize function which opens the file and reads lines from the file.
void init() {
llist *list = (llist *) malloc(sizeof(llist));
//
//bunch of file i/o codes
//
while (read file until it returns NULL) {
add(list, line);
//if I try to print the values of the list here it works.
}
//Outside the loop, the head is back to NULL
}
And another problem that I realized is the values get concatenated every time I try to print the value. That is to say, the output would be:
First Loop: Tony
Second Loop: Peter
TonyPeter
Third Loop: Mir
PeterMir
TonyPeterMir
How do I fix it so the add function permanently adds the node to the linked list?
Why would the values be jumbled up like that?
----EDITED----
The list is a global variable, and here are some more snippets from the init function. This is the while loop with the problem:
//open file
//initialize & declare pointers
while (1) {
for (i = 0; i < max; i++) {
value[i] = '\0';
}
if (!(fgets(value,max,f))) {
//segfaults if I try to print out the list inside this block.
break;
}
add(list, value);
//the values are well separated in this statement
printf("id is %s\n", list->head->value);
//This print list works, but works weird as shown above.
print_list(list);
}
fclose(f);
//This print list doesn't work, the list is NULL
print_list(list);
And this is the print list function:
void print_users(llist *list) {
ListNode *e;
if (list->head == NULL) {
printf("NO USERS\r\n");
return;
}
e = list->head;
while (e != NULL) {
puts(e->id);
e = e->next;
}
}
So I don't have a good grasp at all on what you're exactly trying to do here, so we can only do but so much. You may consider posting a MCVE. However, I may be able to give you some pointers on building a linked list. I directly copied your add function into a linked list class that I just hurriedly built, and it worked fine, so there may be something else in your llist class that is causing the issue, or it could be something else in your code. The class and a brief description of the class are listed below.
basic node class
Note: I used templates, but you could just as easily remove the template statements and replace T with any type.
template<typename T>
class node {
private:
T data;
node* next;
public:
// The C++11 rule of 5
// Default Constructor
node(T value = T(), node* nxt = nullptr) : data(value), next(nxt) { }
// Copy Constructor
node(const node<T>& src) : data(src.data), next(src.next) { }
// Move Constructor
node(node<T>&& src) : data(src.data), next(src.next) {
src.data = T();
src.next = nullptr;
}
// Copy Assignment Operator
node<T>& operator=(const node<T>& src) {
this->data = src.data;
this->next = src.next;
return(*this);
}
// Move Assignment Operator
node<T>& operator=(node<T>&& src) {
this->data = src.data;
this->next = src.next;
src.data = T();
src.next = nullptr;
}
// Destructor
~node() {};
// Some functions to help with encapsulation
void set_next(node* nxt) {
this->next = nxt;
}
void set_data(T value) {
this->data = value;
}
node* get_next() {
return(this->next);
}
T& get_data() {
return(data);
}
};
linked list class body
Since you're using dynamic memory, you need to make sure you adhere to the rule of 3 or 5 depending on whether or not you're using C++11.
template<typename T>
class llist {
private:
node<T>* head;
public:
llist();
llist(const llist& src);
llist(llist&& src);
llist(~llist);
llist& operator=(const llist& src);
llist& operator=(llist&& src);
void push();
void insert();
};
default constructor
Nothing fancy here.
template<typename T>
llist<T>::llist() : head(nullptr) { }
copy constructor
Since you're using dynamic memory this is crucial
template<typename T>
llist<T>::llist(const llist& src) {
node<T>* tmp = src.head;
while(tmp) {
this->push(tmp->get_data());
}
}
move constructor
template<typename T>
llist<T>::llist(llist&& src) {
// delete current nodes
node<T>* tmp = this->head;
while(tmp) {
tmp = head->get_next();
delete head;
head = tmp;
}
// steal the sources list
this->head = src.head;
src.head = nullptr;
}
destructor
template<typename T>
llist<T>::~llist() {
node<T>* tmp = this->head;
while(tmp) {
tmp = head->get_next();
delete head;
head = tmp;
}
}
copy assignment operator
template<typename T>
llist& llist<T>::operator=(const llist<T>& src) {
node<T>* tmp = src.head;
while(tmp) {
this->push(tmp->get_data());
}
return(*this);
}
move assignment operator
template<typename T>
llist& llist<T>::operator=(llist<T>&& src) {
node<T>* tmp = this->head;
while(tmp) {
tmp = head->get_next();
delete head;
head = tmp;
}
this->head = src.head;
src.head = nullptr;
return(*this);
}
push member
this is essentially opposite of your add member.
template<typename T>
void llist<T>push(T data) {
node<T>* new_node = new node<T>(data);
if(this->head) {
node<T>* tmp = this->head;
while(tmp->get_next()) {
tmp = tmp->get_next();
}
tmp->set_next(new_node);
} else {
this->head = new_node;
}
}
insert member
This is essentially your add member.
template<typename T>
void llist<T>insert(T data) {
node<T>* new_node = new node<T>(data, this->head);
this->head = new_node;
}
I don't know if this will help, and you probably already have and know most of this, but I hope it helps.
In this code, it would appear that you attempted to 'malloc' space for a "llist" user defined object.
void init() {
llist *list = (llist *) malloc(sizeof(llist));
//
//bunch of file i/o codes
//
while (read file until it returns NULL) {
add(list, line);
//if I try to print the values of the list here it works.
}
//Outside the loop, the head is back to NULL
}
First, you tagged this as C++. In C++, you simply must use new and delete. The C++ compiler does not associate "malloc" with the ctor / dtor of your user created object called "llist". And I assure you that you really do want to create these two methods, even when each are simple. Really.
On the other hand, the C++ compiler does provide New and Delete, and will automatically invoke the ctor and dtor when appropriate for both dynamic variables (in heap), and automatic variables (on stack). The compiler will not support this with malloc.
Second, your function init() does not return or otherwise deliver the value of the automatic variable you named "list" to any other scope. (Typically, a list lifetime exceeds the life of any function that uses or creates it.)
So your object "list" only exists within the scope of the function init(), within the lifetime of init(). Not very useful.
So the handle to the list of 'malloc'ed things is lost, no longer accessible to anything else. After init(), where did you plan for the listHead to reside?
Even if you used new (and delete) the code still does not deliver the listHead anywhere.
To further your program, you need perhaps 1 of 2 things:
1) a return (from the function) of the "list" handle (I've been calling it "listHead" as you intended, right?)
llist* init() {
llist *listHead = ...
return(listHead);
}
OR
2) a parameter reference which your init function changes. This places the list head outside of init().
void init( llist** listHead) {
llist *list = ...
*listHead = list;
}
You might look into, and take hints from std::list, which has 40+ methods, though you might only need 10. For the methods you plan to implement, you should strive to conform to and use similar names and parameters.
Perhaps you meant to use a class data attribute with the label list (it is quite difficult to imagine this from what you provided). In this case, you should distinguish data attributes names to help you remember what it is, and that it has a different scope. For instance, I would use m_listHead. The prefix m_ (or often, simply the one char prefix '_') simply indicates to the reader that this symbol is a data attribute of a class. This idea is a common c++ idiom, and not enforced by the compiler, but is often part of a coding-standard.
Good luck.
I used gdb to find the exact line with the seg fault. It's noted within the dequeue function as a comment.
Here is the entire queue class.
I seg fault upon calling dequeue() when there are two objects and the sentinal within the queue.
template <typename T>
void Queue<T>::clear()
{
while(!isEmpty())
dequeue();
}
template <typename T>
void Queue<T>::enqueue(const T& x)
{
if(isEmpty())
{
Queue<T>* temp = new Queue<T>;
m_data = x;
m_next = temp;
return;
}
Queue<T>* temp = this;
while(temp->m_next != NULL)
{
temp = temp->m_next;
}
Queue<T>* node = new Queue<T>();
temp->m_data = x;
node->m_next = temp->m_next;
temp->m_next = node;
return;
}
template <typename T>
void Queue<T>::dequeue()
{
if(isEmpty())
return;
if(m_next != NULL)
{
Queue<T>* temp = m_next;
m_data = temp->m_data;
m_next = temp->m_next;
delete temp; //Seg fault here
}
return;
}
template <typename T>
const T& Queue<T>::front() const throw (int)
{
if(isEmpty())
throw 0;
return m_data;
}
template <typename T>
bool Queue<T>::isEmpty() const
{
return (m_next==NULL);
}
template <typename T>
int Queue<T>::size() const
{
int size = 0;
const Queue<T>* temp = this;
while(temp->m_next != NULL)
{
temp = temp->m_next;
size++;
}
return size;
}
Sorry, thought I'd already posted the Queue class:
template <typename T>
class Queue : public AbstractQueue<T> {
public:
Queue(){m_next = NULL;};
virtual void clear();
virtual void enqueue(const T& x);
virtual void dequeue();
virtual const T& front() const throw (int);
virtual bool isEmpty() const;
virtual int size() const;
~Queue(){
clear();
return;
};
private:
T m_data;
Queue* m_next;
};
And it inherits from this class:
template < typename T >
class AbstractQueue
{
public:
// Purpose: clears the queue
// Postconditions: the queue is now empty
// -- PURE VIRTUAL
virtual void clear() = 0;
// Purpose: enqueue an element into the queue
// Parameters: x is the item to add to the queue
// Postconditions: x is now the element at the end of the queue,
// -- PURE VIRTUAL
virtual void enqueue(const T& x) = 0;
// Purpose: dequeues
// Postconditions: the element formerly at the front of the queue has
// been removed
// Dequeueing from an empty Queue produces no errors, queue remains empty.
// -- PURE VIRTUAL
virtual void dequeue() = 0;
// Purpose: looks at the front of the queue
// Returns: a reference to the element currently in front of the queue
// Exception: if the queue is currently empty, throw SOMETHING!!
// -- PURE VIRTUAL
virtual const T& front() const = 0;
// Purpose: Checks if a queue is empty
// Returns: 'true' if the queue is empty
// 'false' otherwise
// -- PURE VIRTUAL
virtual bool isEmpty() const = 0;
// Purpose: Returns the size of a queue.
// Returns: the number of elements in the Queue
// -- PURE VIRTUAL
virtual int size() const = 0;
// ----------------
// Purpose: Destructor
// -- VIRTUAL
virtual ~AbstractQueue() {};
};
In this code:
Queue<T>* temp = m_next;
m_data = m_next->m_data;
m_next = m_next->m_next;
You don't check that m_next is non-null (if you're at the end of the list) so you start dereferencing the null pointer and all bets are off at that point.
To me, the following line inside enqueue() looks little strange.
Queue<T>* node = new Queue<T>();
It is creating a new Queue every time.
Could the intention be the following instead?
T * node = new T;
OK, instead of giving you a fish, I will teach you how to fish...
When you get segmentation fault it means that the operating system has detected a memory access error.
This usually happens in C/C++ when you play with pointers. pointers are very dangerous and have to be treated carefully.
How to detect where the problem occurs ?
Well, Linux is not very informative when your program receives SEGFAULT however, it gives you a lot of information. you just need to know how to "read" it.
The coredump, is a picture of the memory, stack and variables at the moment the segmentation fault occurred. to run it
gdb myapp core
where myapp is your application executable, and core is the coredump.
now you'll see something like:
GNU gdb 19991004
Copyright 1998 Free Software ���.�
Core was generated by `testit'.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /usr/lib/libstdc++-libc6.1-1.so.2...done.
Reading symbols from /lib/libm.so.6...done.
Reading symbols from /lib/libc.so.6...done.
Reading symbols from /lib/ld-linux.so.2...done.
#0 0x823221a in main () at blabla.c:3
10 *i++;
it will show you exactly which line caused the fault.
If you want to know exactly how you got to that line, type bt
this will show you the backtrace, from your application main() until the actual fault, including parameters passed to the functions.
I think that once you'll know exactly where the segmentation fault occurred it will be much easier for you to solve it.
Few notes:
If coredump is not created.
type this in the console:
ulimit -c unlimited
You need to compile your program with -g to enable meaningful names of symbols in gdb.
I have a generic list with a template
template<class t>
class GenericList {
//the data is storeed in a chained list, this is not really important.
struct c_list { t data; c_list* next; ...constructor... };
public:
bool isDelete;
GenericList() : isDelete(false) {...}
void add(t d) {
c_list* tmp = new c_list(d, first->next);
//this is not really important again...
}
~GenericList() {
c_list* tmp = first;
c_list* tmp2;
while(tmp->next!=NULL) {
if (isDelete) { delete tmp->data; } //important part
tmp2=tmp->next;
delete tmp;
tmp=tmp2;
}
}
};
The important part is the isDelete
This is only a sample code
I need this because I want to store data like this:
GenericList<int> list;
list.add(22);list.add(33);
and also
GenericList<string*> list;
list.add(new string("asd")); list.add(new string("watta"));
The problem if I store only <int> the compiler said that I cannot delete non pointer variables, but I don't want to in this case. How can I solve this?
when I store <int*> there is no compiler error...
Without changing much your code, I would solve your problem as
template<class t>
class GenericList
{
//same as before
//add this function template
template<typename T>
void delete_if_pointer(T & item) {} //do nothing: item is not pointer
template<typename T>
void delete_if_pointer(T* item) { delete item; } //delete: item is pointer
~GenericList() {
c_list* tmp = first;
c_list* tmp2;
while(tmp->next!=NULL) {
delete_if_pointer(tmp->data); // call the function template
tmp2=tmp->next;
delete tmp;
tmp=tmp2;
}
}
};
EDIT: I just noticed that #ildjarn has provided similar solution. However there is one interesting difference: my solution does NOT require you to mention the type of data when calling the function template; the compiler automatically deduces it. #ildjarn's solution, however, requires you to mention the type explicitly; the compiler cannot deduce the type in his solution.
I would create a nested struct template inside your class to help:
template<typename U>
struct deleter
{
static void invoke(U const&) { }
};
template<typename U>
struct deleter<U*>
{
static void invoke(U* const ptr) { delete ptr; }
};
Then change the line that was using isDelete from
if (isDelete) { delete tmp->data; }
to
if (isDelete) { deleter<t>::invoke(tmp->data); }
delete on an int makes a program ill-formed, so the compiler will reject it, even though the delete would never be reached.
What you want is only possible if you switch from "bare" pointers to smart pointers such as unique_ptr or shared_ptr; those handle memory management for you, without explicit delete.