For academic purposes, I'm trying to develop a little "textual adventure game". I have to implement all data structures by my own. Now, I have some problems with the implementation of a generic (template) LinkedList.
In the specific, this data structure works with everything (primitive data types and custom objects) BUT strings! (standard library strings).
When I try to add strings to a list, the application crashes with the following error (in console):
"terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_S_constructor null not valid"
The list is implemented as a "double linked list" using the head-node as first-last node
Here the code ("Abstract" List interface):
#ifndef LIST_H_
#define LIST_H_
template <class T>
class List
{
public:
virtual ~List() {}
virtual T get(int position) = 0;
virtual List* add(T item) = 0;
virtual List* insert(T item, int position) = 0;
virtual List* remove(int position) = 0;
virtual int size() const = 0;
virtual bool isEmpty() const = 0;
protected:
private:
};
#endif /* LIST_H_ */
This is the LinkedList implementation (the "node" class):
#include "List.h"
#include <stdlib.h>
#ifndef LINKEDLIST_H_
#define LINKEDLIST_H_
template <class T>
class ListNode
{
public:
ListNode(T item)
{
mItem = item;
mNext = NULL;
mPrev = NULL;
}
ListNode(T item, ListNode<T>* next, ListNode<T>* prev)
{
mItem = item;
mNext = next;
mPrev = prev;
}
~ListNode()
{
delete &mItem;
}
T getItem()
{
return mItem;
}
ListNode<T>* getNext()
{
return mNext;
}
ListNode<T>* getPrev()
{
return mPrev;
}
void setItem(T item)
{
mItem = item;
}
void setNext(ListNode<T>* next)
{
mNext = next;
}
void setPrev(ListNode<T>* prev)
{
mPrev = prev;
}
protected:
private:
T mItem;
ListNode<T> *mNext, *mPrev;
};
The LinkedList class:
template <class K>
class LinkedList : public List<K>
{
public:
LinkedList()
{
mSize = 0;
mFirstNode = NULL;
}
~LinkedList()
{
// implementazione distruttore tramite ciclo sui nodi
}
K get(int position)
{
K item = NULL;
ListNode<K>* targetNode = getNodeAtPosition(position);
if (targetNode != NULL) item = targetNode->getItem();
return item;
}
List<K>* add(K item)
{
if (mFirstNode == NULL)
{
mFirstNode = new ListNode<K>(item);
mFirstNode->setNext(mFirstNode);
mFirstNode->setPrev(mFirstNode);
}
else
{
ListNode<K>* newNode = new ListNode<K>(item, mFirstNode, mFirstNode->getPrev());
mFirstNode->getPrev()->setNext(newNode);
mFirstNode->setPrev(newNode);
}
mSize++;
return this;
}
List<K>* insert(K item, int position)
{
ListNode<K>* targetNode = getNodeAtPosition(position);
if (targetNode != NULL)
{
ListNode<K>* newNode = new ListNode<K>(targetNode->getItem(), targetNode->getNext(), targetNode);
targetNode->setItem(item);
targetNode->setNext(newNode);
mSize++;
}
return this;
}
List<K>* remove(int position)
{
ListNode<K>* targetNode = getNodeAtPosition(position);
if (targetNode != NULL)
{
targetNode->setItem(targetNode->getNext()->getItem());
targetNode->setNext(targetNode->getNext()->getNext());
//delete targetNode->getNext();
mSize--;
}
return this;
}
int size() const
{
return mSize;
}
bool isEmpty() const
{
return (mFirstNode == NULL) ? true : false;
}
protected:
ListNode<K>* getNodeAtPosition(int position)
{
ListNode<K>* current = NULL;
if (mFirstNode != NULL && position < mSize)
{
current = mFirstNode;
for (int i = 0; i < position; i++)
{
current = current->getNext();
}
}
return current;
}
private:
int mSize;
ListNode<K>* mFirstNode;
};
#endif /* LINKEDLIST_H_ */
Suggestions?
Part of your problem is here:
ListNode(T item)
{
mItem = item; // for a std::string, this will be a class member, non-pointer
mNext = NULL;
mPrev = NULL;
}
ListNode(T item, ListNode<T>* next, ListNode<T>* prev)
{
mItem = item; // same here
mNext = next;
mPrev = prev;
}
~ListNode()
{
delete &mItem; // you are attempting to delete an item you never created
}
You should either change your constructors to create a T* object on the heap (which will then be deleted in your destructor), or remove the delete line from your destructor.
This problem will be evident with far more than just std::string, by the way.
Somewhere in your program you are doing this:
std::string s(nullptr);
Calling std::string's constructor with a null pointer is causing it to throw a std::logic_error exception.
From the standard:
ยง 21.4.2
basic_string(const charT* s, size_type n, const Allocator& a = Allocator());
Requires: s shall not be a null pointer and n < npos.
It seems it's not possible to pass std::string as template argument...
Strings as Template Arguments
Now I use an "old" - char const* - to achieve the expected result, even if I have to implement my personal "utils" methods to work with those pointers now...
Related
I am trying to implement a linked list, and I am completely lost. I'm getting break points all over the place, specifically with the erase method. Whenever I alter the erase method, some error will inevitably spring up. I've got pointer errors, problems with the destructor that only occur when the erase method is called, and so on.
Here's what I have so far:
Header file:
#pragma once
class IntList {
private:
class IntNode {
public:
IntNode(int v, IntNode *pr, IntNode *nx);
~IntNode();
IntNode* previous;
IntNode* next;
class iterator {
public:
iterator(IntNode* t);
int& operator*();
iterator& operator++();
iterator& operator--();
bool operator!=(iterator other)const;
private:
IntNode* target;
};
private:
int value;
};
IntNode* head;
IntNode* tail;
int count;
public:
IntList();
~IntList();
void push_back(int v);
void pop_back();
int size() const { return count; }
typedef IntNode::iterator iterator;
iterator begin();
iterator end();
//unsigned int size() const;
void push_front(int value);
bool empty() const;
int& front();
int& back();
void clear();
iterator erase(iterator position);
};
Implementation:
#include "IntList.h"
#include <stdexcept>
IntList::IntList() : head{ nullptr }, tail{ nullptr }, count{ 0 }
{}
IntList::~IntList() {
while (head) {
head = head->next;
delete head;
}
}
void IntList::push_back(int v) {
tail = new IntNode{ v, tail, nullptr };
if (!head) { head = tail; }
count += 1;
}
void IntList::pop_back() {
tail = tail->previous;
delete tail->next;
count -= 1;
}
IntList::iterator IntList::begin()
{
return iterator{ head };
}
IntList::iterator IntList::end() {
return iterator{ nullptr };
}
void IntList::push_front(int value) {
head = new IntNode{ value, nullptr, head };
if (!tail) { tail = head; }
count += 1;
}
bool IntList::empty() const{
return (count==0);
}
int& IntList::front() {
return *begin();
}
int& IntList::back() {
return *begin();
}
void IntList::clear() {
head = nullptr;
tail = nullptr;
count = 0;
}
IntList::iterator IntList::erase(iterator position) {
int midpointL = 0;
for (iterator index = begin(); index != position; ++index) {
midpointL++;
}
if (midpointL == 0) {
head = head->next;
}
else if (midpointL == count) {
tail = tail->previous;
}
else {
// Move head to get a reference to the component that needs to be deleted
for (int i = 0; i < midpointL; i++) {
head = head->next;
}
// Change the previous and next pointers to point to each other
(head->previous)->next = (head->next);
(head->next)->previous = (head->previous);
for (int i = midpointL-1; i > 0; i++) {
head = head->previous;
}
}
count-=1;
return position;
}
IntList::IntNode::IntNode(int v, IntNode * pr, IntNode * nx)
: previous{ pr }, next{ nx }, value{ v }
{
if (previous) { previous->next = this; }
if (next) { next->previous = this; }
}
IntList::IntNode::~IntNode() {
if (previous) previous->next = next;
if (next) next->previous = previous;
}
IntList::IntNode::iterator::iterator(IntNode* t)
: target{ t }
{}
int& IntList::IntNode::iterator::operator*() {
if (!target) { throw std::runtime_error{ "Deferenced sentinel iterator." }; }
return target->value;
}
IntList::IntNode::iterator& IntList::IntNode::iterator::operator++()
{
if (target) { target = target->next; }
return *this;
}
IntList::IntNode::iterator& IntList::IntNode::iterator::operator--()
{
if (target) { target = target->previous; }
return *this;
}
bool IntList::IntNode::iterator::operator!=(iterator other)const
{
return (!(target == other.target));
}
Could anyone help point me in the right direction?
Thanks!
Let's make a kind of quick review here:
IntList::~IntList() {
while (head) {
head = head->next;
delete head;
}
}
you should do instead:
IntList::~IntList() {
while (head) {
IntNode* newHead = head->next;
delete head;
head = newHead;
}
}
as you are deleting the "next" object and then you are trying to get access to it in the next iteration.
void IntList::pop_back() {
tail = tail->previous;
delete tail->next;
count -= 1;
}
Here you are not checking if tail is null or if it is pointing to head..(what's the empty condition?), maybe count!=0? in case you may delete a not existing next-node
IntList::iterator IntList::end() {
return iterator{ nullptr };
}
..end is null? ebd should be your tail...
int& IntList::back() {
return *begin();
}
that's begin..not back.
void IntList::clear() {
head = nullptr;
tail = nullptr;
count = 0;
}
a clear should release all the objects in the list. You are generating garbage here (leaks).
I stopped here, sorry for that, it's just a coffee break. But you should look carefully at:
* null pointer usage
* delete your node list item when not needed
* pay attention to do not use invalid pointers (like head->previous->next I saw somewhere)
You have to review your code, bottom up. Hope that those first hints help you through your learning process.
Have fun,
Ste
How can I implement for_each for that implementation of Node and List?
I'm sure it's not long code, can some share a few strokes of his knowledge please? Do I need to implement it as template or just as inner function.
Thanks for helping.
class Node {
int data;
Node *next;
public:
Node() {}
void SetData(int aData) { data = aData; }
void SetNext(Node *aNext) {
next = aNext;
}
int Data() { return data; }
Node* Next() { return next; }
};
// List class
class List {
Node *head;
public:
List() { head = nullptr; }
void Append(int data){
// Create a new node
Node *newNode = new Node();
newNode->SetData(data);
newNode->SetNext(nullptr);
// Create a temp pointer
Node *tmp = head;
if ( tmp != nullptr ) {
// Nodes already present in the list
// Parse to end of list
while ( tmp->Next() != nullptr) {
tmp = tmp->Next();
}
// Point the last node to the new node
tmp->SetNext(newNode);
}
else {
// First node in the list
head = newNode;
}
}
void Delete(int data){
// Create a temp pointer
Node *tmp = head;
// No nodes
if ( tmp == nullptr ) return;
// Last node of the list
if ( tmp->Next() == nullptr ) {
delete tmp;
head = nullptr;
}
else {
// Parse thru the nodes
Node* prev;
do {
if ( tmp->Data() == data ) break;
prev = tmp;
tmp = tmp->Next();
} while ( tmp != nullptr );
// Adjust the pointers
if (tmp->Next()!=nullptr) prev->SetNext(tmp->Next());
else prev->SetNext(nullptr);
// Delete the current node
if (tmp!=nullptr) delete tmp;
}
}
};
EDIT:
This is use of iterator:
for (List<Pair, CompareFunction>::myIterator it = this->_pairs.begin();
it != this->_pairs.end(); ++it) {
Pair cur_pair = *it;
if (cur_pair.first == key) {
this->_pairs.Delete(cur_pair);
this->_size--;
}
}
This is Pair Class as sub-class in another template class:
template <class KeyType, class ValueType, class CompareFunction = std::less<KeyType> >
class MtmMap {
public:
class Pair {
public:
Pair():first(KeyType()){} ////////////////////
Pair(const KeyType& key, const ValueType& value)
: first(key), second(value) {}
const KeyType first;
ValueType second;
~Pair() = default;
In our concrete case KeyType and ValueType run as both int.
Add the following code into your List class:
class List{
...
class myIterator
{
public:
typedef myIterator self_type;
typedef Node* pointer;
myIterator(pointer ptr) : ptr_(ptr) { }
self_type operator++() {
self_type i = *this;
if (ptr_) ptr_ = ptr_->Next();
return i;
}
int operator*() { if (ptr_ ) return ptr_->Data(); else return 0; }
bool operator==(const self_type& rhs) {
if (!ptr_ && !rhs.ptr_) return true;
return (ptr_ && rhs.ptr_ && rhs.ptr_->Data()==ptr_->Data());
}
bool operator!=(const self_type& rhs) {
return !(*this==rhs);
}
private:
pointer ptr_ = nullptr;
};
myIterator begin() { return myIterator(head); }
myIterator end() { return myIterator(nullptr); }
};
You may then use normal iterators like in the following example:
List l;
l.Append(2);
l.Append(5);
l.Append(7);
l.Append(11);
for (int i: l) std::cout << i << std::endl;
//or
for (myIterator it=l.begin(); it!=l.end(); ++it) std::cout << *it << std::endl;
I have to ADD a node to the end of a Linked LIST, it's not throwing any errors so far, but apparently it's not working too. I've looked into others answers, but couldn't see what's wrong with mine.
I think that the problem might be with getNext() and NULLs.
ps: I'm using HPP
Here's the method :
// ADD a node to the end of the Linked list
void add(const T& dado)
{
Elemento < T > *novo = new Elemento<T>(dado, NULL);
if (novo == NULL)
{
throw 2;
}
if (head->getNext() != NULL)
{
Elemento < T > *auxi = new Elemento<T>(dado, head->getNext());
int i;
for (i = 0; auxi->getNext() == NULL; i++)
{
auxi->setNext(auxi->getNext());
if (auxi->getNext()() == NULL)
{
size++;
auxi->setNext(novo);
}
}
}
else
{
size++;
head->setNext(novo);
}
}
My elemento class is as follow:
#ifndef ELEMENTO_HPP
#define ELEMENTO_HPP
template<typename T>
class Elemento {
private:
T *info;
Elemento<T>* _next;
public:
Elemento(const T& info, Elemento<T>* next) : info(new T(info)), _next(next) {}
~Elemento() {
delete info;
}
Elemento<T>* getNext() const {
return _next;
}
T getInfo() const {
return *info;
}
void setNext(Elemento<T>* next) {
_next = next;
}
};
#endif
You can see the whole code here: http://pastebin.com/7yJfsK8j
(method names in Portuguese, but there are comments to explain).
Try this for-loop:
Elemento<T> *ptr;
//Will iterate until ptr-> getNext() is null (this means ptr is not null).
for(ptr = head; ptr -> getNext() != NULL; ptr = ptr -> getNext())
{
//Does nothing.
};
ptr -> setNext(novo);
size++;
Hope it works!
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I'm trying to test out the code my professor gave us. We have to change the implementation of the code but that is not what I am stuck on. I am stuck on making a working test code. He gave me the test code to run but when I try to run it I keep getting errors which shouldn't be the case. Could anyone tell me what the problem is in my test code so I can start changing and adding different functions into my code? Thanks
Here is my code:
// ListNode.h
#ifndef _LISTNODE_H
#define _LISTNODE_H
#include <cstdlib>
typedef int ItemType;
class ListNode {
friend class LList;
public:
ListNode(ItemType item, ListNode* link = NULL);
private:
ItemType item_;
ListNode *link_;
};
inline ListNode::ListNode(ItemType item, ListNode *link)
{
item_ = item;
link_ = link;
}
#endif // _LISTNODE_H
// LList.h
#ifndef _LLIST_H
#define _LLIST_H
#include "ListNode.h"
class LList {
public:
LList();
LList(const LList& source);
~LList();
LList& operator=(const LList& source);
int size() { return size_; }
void append(ItemType x);
void insert(size_t i, ItemType x);
ItemType pop(int i = -1);
ItemType& operator[](size_t position);
private:
// methods
void copy(const LList &source);
void dealloc();
ListNode* _find(size_t position);
ItemType _delete(size_t position);
// data elements
ListNode *head_;
int size_;
};
#endif // _LLIST_H
// LList.cpp
#include "LList.h"
LList::LList()
{
head_ = NULL;
size_ = 0;
}
ListNode* LList::_find(size_t position)
{
ListNode *node = head_;
size_t i;
for (i = 0; i<position; i++) {
node = node->link_;
}
return node;
}
ItemType LList::_delete(size_t position)
{
ListNode *node, *dnode;
ItemType item;
if (position == 0) {
dnode = head_;
head_ = head_->link_;
item = dnode->item_;
delete dnode;
}
else {
node = _find(position - 1);
if (node != NULL) {
dnode = node->link_;
node->link_ = dnode->link_;
item = dnode->item_;
delete dnode;
}
}
size_ -= 1;
return item;
}
void LList::append(ItemType x)
{
ListNode *node, *newNode = new ListNode(x);
if (head_ != NULL) {
node = _find(size_ - 1);
node->link_ = newNode;
}
else {
head_ = newNode;
}
size_ += 1;
}
void LList::insert(size_t i, ItemType x)
{
ListNode *node;
if (i == 0) {
head_ = new ListNode(x, head_);
}
else {
node = _find(i - 1);
node->link_ = new ListNode(x, node->link_);
}
size_ += 1;
}
ItemType LList::pop(int i)
{
if (i == -1) {
i = size_ - 1;
}
return _delete(i);
}
ItemType& LList::operator[](size_t position)
{
ListNode *node;
node = _find(position);
return node->item_;
}
LList::LList(const LList& source)
{
copy(source);
}
void LList::copy(const LList &source)
{
ListNode *snode, *node;
snode = source.head_;
if (snode) {
node = head_ = new ListNode(snode->item_);
snode = snode->link_;
while (snode) {
node->link_ = new ListNode(snode->item_);
node = node->link_;
snode = snode->link_;
}
size_ = source.size_;
}
LList& LList::operator=(const LList& source)
{
if (this != &source) {
dealloc();
copy(source);
}
return *this;
}
LList::~LList()
{
dealloc();
}
void LList::dealloc()
{
ListNode *node, *dnode;
node = head_;
while (node) {
dnode = node;
node = node->link_;
delete dnode;
}
}
#include "LList.h"
#include <iostream>
using namespace std;
int main()
{
LList b, c;
int x;
b.append(1);
b.append(2);
b.append(3);
c.append(4);
c.append(5);
c = b;
x = b.pop();
cout << c;
}
Could anyone help me write a working test code, this the last thing I will need to start adding my different functions.
I keep getting this error:
Error 1 error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'LList' (or there is no acceptable conversion) c:\users\koopt_000\documents\visual studio 2013\projects\lab10\lab10\testlist.cpp 18 1 Lab10
Any help?
In your code:
cout << c;
is the problem. You cannot print your linked list that way (Edit: Unless you have an overload for the operator << which does not appear in your code).
For printing the elements, you can iterate through the list starting from the first node to the last. Something like that would work:
void LList::printList()
{
ListNode *tmp = head_;
while(tmp) {
std::cout<<tmp->item_;
tmp=tmp->link_;
}
}
PS: don`t forget the put the method prototype into the class definition. And of course in your main method can call to the function as follows:
...
c.printList();
....
Hope that helps!
You are attempting to output your LList, but it's not quite that straight forward. You need to overload the output operator:
friend std::ostream& operator<< (std::ostream& stream, const LList& list) {
// Write code here to iterate through the list
// and output each item...
// Return the stream so we can chain outputs..
return stream;
}
I'm running into some problems while trying to use a virtual function within my classes.
I'm using a Linked List to store Employee, Staff and Managers - which inherit each other (Staff and Managers inherit Employee base class).
I need to be able to access a function called getType which returns either "Staff Member" or "Manager" based on which class it is.
this snipit of code is my creation of staff and managers.
Staff staff4 = { "Lisa", "22/02/2012", 0004, HR, 8.9, 34.50 };
Employee* pStaff4 = &staff4;
Employee& testStaff4 = staff4;
myList->addInFront(testStaff4);
Staff staff5 = { "Jade", "23/03/2014", 0003, HR, 6.4, 38.50 };
Employee* pStaff5 = &staff5;
Employee& testStaff5 = staff5;
myList->addInFront(testStaff5);
Manager manager1 = { "Lily", "01/09/2012", 0001, MARKETING, 75968 };
Employee* pMan1 = &manager1;
Employee& testMan1 = manager1;
myList->addInFront(testMan1);
Manager manager2 = { "Craig", "27/03/2011", 0002, HR, 82478 };
Employee* pMan2 = &manager2;
Employee& testMan2 = manager2;
myList->addInFront(testMan2);
//cout << pStaff5->getType();
//system("pause");
This is my employee.h (i've taken out other functions to save space on this post)
class Employee
{
protected:
string name;
string startDate;
unsigned long empNumber;
string dept;
public:
Employee() {};
Employee(string, string, unsigned long, string);
virtual const string getType()
{
return "Emp";
}
};
class Manager : public Employee
{
private:
unsigned long salary;
public:
virtual const string getType()
{
return "Manager";
}
};
class Staff : public Employee
{
private:
float hourlyRate;
float hoursPerWeek;
public:
const string getType()
{
return "Staff Member";
}
};
and finally this is how i'm attempting to call the getType() function:
void displayList(const List& list)
{
List temp(list);
while (!temp.isEmpty())
{
cout << temp.first()->item.getType() << "\n";
cout << temp.first()->item.getName() << "\n";
temp.deleteFirst();
}
}
Here is my list header and .cpp
list.h
//#include <string>
#include "Employees.h"
#define Item Employee
using namespace std;
struct Node
{
Item item;
Node* next;
};
class List
{
private:
Node* head;
Node* end() const;
void copy(const List&);
void destroy();
public:
List();
List(const List&);
~List();
List& operator=(const List&);
bool operator==(const List&);
bool isEmpty() const;
Node* first() ;
Item last() const;
List tail() const;
void addInFront(const Item&);
void addAtEnd(const Item&);
void deleteFirst();
Node* search(const long);
bool searchDelete(const long);
};
list.cpp
#include "stdafx.h"
#include "List.h"
#include <assert.h>
List::List()
{
head = NULL;
}
List::List(const List& otherList) : head(nullptr)
{
copy(otherList);
}
bool List::isEmpty() const
{
return (head == nullptr);
}
Node* List::first()
{
assert(head != nullptr);
return head;
}
void List::deleteFirst()
{
if (head != NULL)
{
Node* tmp = head->next;
delete head;
head = tmp;
}
}
void List::addInFront(const Item& data)
{
Node* nodePtr = new Node;
assert(nodePtr != nullptr);
nodePtr -> item = data;
nodePtr ->next = head;
head = nodePtr;
}
Node* List::search(const long longID)
{
}
bool List::searchDelete(const long longID)
{
Node *temp, *prevNode;
temp = head;
prevNode = NULL;
while (temp != NULL)
{
}
}
Node* List::end() const
{
if (head == nullptr)
return nullptr;
else
{
Node* nodePtr = head;
while (nodePtr->next != nullptr)
{
nodePtr = nodePtr->next;
}
return nodePtr;
}
}
void List::addAtEnd(const Item& data)
{
Node* nodePtr = new Node;
assert(nodePtr != nullptr);
if (head == nullptr)
{
head = nodePtr;
nodePtr->item = data;
}
else
{
nodePtr->item = data;
Node* ptr = end();
ptr->next = nodePtr;
}
}
List& List::operator=(const List& rhs)
{
if (&rhs != this)
{
destroy();
copy(rhs);
}
return *this;
}
void List::copy(const List& otherList)
{
}
void List::destroy()
{
while (head != nullptr)
{
Node* ptr = head;
head = head->next;
delete ptr;
}
}
List::~List()
{
}
apologies about the length of these files.
I'm confused as to why it won't call the appropriate virtual function, as you can see in the first code snipit that i used pStaff5->getType() and that worked - however I can't access the nodes like that once i've stored them in a linked list...(can I?)
Kind regards
Craig
Your list nodes store an Item but that is only the base class. When you try to put a Manager or Staff in the list only the base class part of the object is copied into the list (this is called "slicing") and not the derived parts of the object.
When you call the virtual function you only get the base class overrider for the virtual, because the object stored in the list is only an Employee.
(You should consider making Node and List into templates, instead of doing #define Item Employee)