When I run the following I miss the last data in the list and get the previous data. When I add a counter and try to subtract by 1 I crash. Any help on this would be much appreciated.
template <typename T>
Iterator<T> Iterator<T>::operator--()
{
ptr = ptr->backward;
return *this;
}
template <typename T>
Iterator<T> DoublyLinkedList<T>::end() const
{
Iterator<T> iObj;
iObj.ptr = this->last;
iObj.capacity = this->count;
return iObj;
}
int main() {
DoublyLinkedList<int> *d = new DoublyLinkedList<int>;
for (int i = 2; i <= 20; i += 2) {
d->insertLast(i);
}
//Get an Iterator which points at the end of the list
Iterator<int> iter = d->end();
--iter;
//Test that it does point to the first
checkTest("testIteratorsDecrement #1", 20, *iter);
//Test that our Iterator can move forward;
--iter;
checkTest("testIteratorsDecrement #2", 18, *iter);
//move it some more
for (int i = 0; i < 7; i++) {
--iter;
}
checkTest("testIteratorsDecrement #3", 4, *iter);
--iter;
checkTest("testIteratorsDecrement #4", 2, *iter);
delete d;
return 0;
}
I try to fix it by doing the following but it crashes. count is a protected int.
template <typename T>
Iterator<T> DoublyLinkedList<T>::end() const
{
Iterator<T> iObj;
iObj.ptr = this->last + (count -1);
iObj.capacity = this->count;
return iObj;
}
Usually end() would return a sentinel value which can not be dereferenced. It looks like you are returning a pointer to the last entry, that is the source of your off-by-one error.
As the implementor you can choose your sentinel value but it should not be a valid entry in the list.
As an aside: I don't see a good reason for having a capacity member in an iterator. How is it kept up-to-date?
Unlike arrays or vector, list does not store its elements one-by-one in memory.
Your first element may be at some begin address, but the next element can be begin+5, or begin-10 or something else.
Basically, this means you can't do arithmetic operations with pointers to elements of your list.
If you want some kind end element, I'd suggest making the last element of your list to point to NULLPTR
Related
This is an insert() function of an implementation of a HashTable with chaining. In order to avoid duplications in the linked_list I ckecked if a value already exists. If it does then I just replace the existing value as it can be seen almost at the end where it comments "update value". That line issues an exception telling me that the iterator is not dereferenceable. Why can't I dereference the iterator returned by std::find()? Is there another way to update the value that was found?
virtual void insert(const K& k, const V& v) {
auto index = hashFctn(k, m_table.capacity());
if (needsToGrow() || m_table[index].m_list.size() >= m_load_factor) {
rehash();
insert(k, v);
}
else {
auto it = std::find(m_table[index].m_list.begin(),
m_table[index].m_list.end(), v);
if (it != m_table[index].m_list.end()) { // if found add it
m_table[index].m_flag = flag::IN_USE;
m_table[index].m_key = k;
m_table[index].m_list.push_back(v);
m_nbrOfElements++;
} else {
*it = v; // update value if exists
}
}
}
You have
if (it != m_table[index].m_list.end()) { // if found add it
// Irrelevant...
} else {
*it = v; // update value if exists
}
If the iterator it is not the end-iterator you do some irrelevant things. But in the else case the iterator it is equal to the end-iterator, and that is not dereferencable. And yet you dereference it.
I think the condition should be the opposite, using == instead.
I am new in programming c++, so please don't be angry with me if my source code is not exactly brilliant.
I have to write a programm to handle with nodes and edges in a graph for my studies.
I have 2 std::lists in my source code. The first one is to store general Nodes and the other one for saving the kind class of my nodes called ArticleNodes. In general, all elements are pointers to the created objects.
To figure out whether one object is the same in the other list I save the memory address and compare it to the elements on the second list. If there is a match the second element will be deleted.
Now I'd like to delete one element in both lists:
void Graph::deleteNode(unsigned int nodeNumber)
{
list<Node*>::iterator it = m_nodes.begin();
ArticleNode* pCurrentArticleNode;
for(unsigned int i=1; i<nodeNumber; i++) { it++; }
Node* pCurrentNode = (*it);
for (list<ArticleNode*>::iterator itArticle = m_articlenode.begin(); itArticle != m_articlenode.end(); itArticle++)
{
pCurrentArticleNode = (*itArticle);
if(pCurrentNode==pCurrentArticleNode) { m_articlenode.remove(pCurrentArticleNode); }
}
m_nodes.remove(pCurrentNode);
delete pCurrentNode;
delete pCurrentArticleNode;
}
I can compile this, but when I call the function, my programm just exits with return 1.
Actually, I figured out that the remove-command in the if-clause is the problem. Why does that not work??
You should use algorithms more than doing everything manually:
void Graph::deleteNode(unsigned int nodeNumber)
{
assert (nodeNumber < m_nodes.size());
auto it = std::next( m_nodes.begin(), nodeNumber - 1 );
auto itArticle = std::find( m_articlenode.begin(), m_articlenode.end(), *it );
if( itArticle != m_articlenode.end() )
m_articlenode.erase( itArticle );
delete *it;
m_nodes.erase(it);
}
Btw your code deletes the same object twice.
When you remove an element from std::list object with remove( ) method, all iterators pointing to that elements become invalid. In your case, after you remove an element from the list m_articlenode, the iterator object itArticle becomes invalid. And when you increment that iterator, you get an undefined behavior.
Pay attention that the method remove( ) deletes all of the items in the list with given value. So you don't need the for-loop at all. Here is the fixed version of your function:
void Graph::deleteNode(unsigned int nodeNumber)
{
list<Node*>::iterator it = m_nodes.begin();
for(unsigned int i=1; i<nodeNumber; i++) { it++; }
Node* pCurrentNode = (*it);
m_articlenode.remove(pCurrentNode);
m_nodes.remove(pCurrentNode);
delete pCurrentNode;
}
You have to use the erase method of std::list in order to remove an element from your list while iterating over it.
This should do the trick:
void Graph::deleteNode(unsigned int nodeNumber)
{
list<Node*>::iterator it = m_nodes.begin();
ArticleNode* pCurrentArticleNode;
for(unsigned int i=1; i<nodeNumber; i++) { it++; }
Node* pCurrentNode = (*it);
list<ArticleNode*>::iterator itArticle = m_articlenode.begin();
while(itArticle != m_articlenode.end()) {
pCurrentArticleNode = (*itArticle);
if(pCurrentNode==pCurrentArticleNode) {
m_articlenode.erase(itArticle++);
} else {
itArticle++;
}
}
m_nodes.remove(pCurrentNode);
delete pCurrentNode;
delete pCurrentArticleNode;
}
Simple issue in your code is that if you go into the if condition is met and it's body executed, you should come out of the loop. std::remove invalidates the iterator ann you will get issues in next iteration so do:
for (list<ArticleNode*>::iterator itArticle = m_articlenode.begin(); itArticle != m_articlenode.end(); itArticle++)
{
pCurrentArticleNode = (*itArticle);
if(pCurrentNode==pCurrentArticleNode)
{
m_articlenode.remove(pCurrentArticleNode);
break;
}
}
In general, there are other issues in the code. As a first step I would suggest using shared_ptr for managing your Nodes and have list of shared_ptr instead of list of pointers.
I am new in programming c++, so please don't be angry with me if my
source code is not exactly brilliant.
We all start somewhere.
Now I'd like to delete one element in both lists:
OK. Quick question. Why are you deleting article node if it is already deleted (via base Node)? I'm assuming for now node is not duplicated in the list:
My solution below... I've passed the lists as arguments. See comments:
#include <list>
#include <algorithm>
struct Node
{
virtual ~Node(){} //For dyna cast to work...
};
struct ArticleNode : Node
{
};
void deleteNode(std::list<ArticleNode*>& articleList, std::list<Node*>& m_nodes, unsigned int nodeNumber)
{
using namespace std;
if (m_nodes.size() > nodeNumber)
{
auto it = m_nodes.begin();
// Advance advances our iterator by N. No need for your for loop - less risk...
std::advance(it,nodeNumber);
Node* currentNode = *it;
//Casting is bad here, but hey, lets assume if type is wrong, we only erase
// it from Node...(Your call)?
ArticleNode* currentArticleNode = dynamic_cast<ArticleNode*>(currentNode);
if (currentArticleNode)
{
//Use find here.... KISS
auto foundPos = std::find(articleList.begin(), articleList.end(), currentArticleNode);
if (foundPos != articleList.end())
{
//No need to delete currentArticleNode, as we're deleting it already...
articleList.erase(foundPos);
}
//Assuming only one item for now...
}
//Else our node was obviously not the right type, and cannot exist in articleNodes...
m_nodes.erase(it);
delete currentNode;
}
else
{
std::cout << "No such node: " << nodeNumber << std::endl;
}
}
I have the following class (which obviously does not yet work as intended):
class A
{
private:
std::vector<int> firstVector, secondVector;
public:
std::vector<int>::iterator begin(){
return firstVector.begin();
}
std::vector<int>::iterator end(){
return secondVector.end();
}
};
How can I define an iterator which will go over the two member containers subsequently, e.g. after firstVector.end()-1 secondVector.begin() is returned and going all the way to secondVector.end() ?
Basically you need to define some custom iterator that internally checks for the end of the first range, then goes on to the next one.
However, this sort of stuff occurs a lot. Eventually you'd ask why an iterator for two vectors, why an iterator for two vectors, why a sequence of the same container type, and so on. Nir Tzachar & I have written a C++ port of Python itertools that does this sort of common stuff. In this case, you'd just use
chain(firstVector, secondVector)
It can be downloaded from this bitbucket repo.
Nothing to stop you from rolling your own. Can even make it random access!
struct chain_iterator
: std::iterator<std::random_access_iterator_tag, int>
{
using it = std::vector<int>::iterator;
std::pair<it, it> v1, v2;
bool first;
it cur;
};
We keep the initial iterator pairs so that we can do random access correctly.
Incrementing is what you'd expect:
chain_iterator& operator++() {
++cur;
if (first && cur == v1.second) {
first = false;
cur = v2.first;
}
return *this;
}
Dereference is trivial:
int& operator*() { return *cur; }
Advance has to do some extra checking:
chain_iterator& operator+=(size_t n) {
if (!first) {
// trivial case
cur += n;
}
else {
size_t d = v1.second - cur;
if (d < n) {
cur += n;
}
else {
first = false;
cur = v2.first + (d - n);
}
}
return *this;
}
I'll leave the rest of the operations as an exercise.
You could write your own function to do the incrementing:
std::vector<int>::iterator& inc(std::vector<int>::iterator& it) {
++it;
if (it == firstVector.end())
it = secondVector.begin();
return it;
}
This is also a good indication to others that the increment doesn't happen normally.
When I am doing practice on leetcode, I met a problem like this:
I used a stl::list container as cache for LRU algorithm. But the sequence of erasing an item and inserting an item made the result different.
I know that it is actually a double list as stl::list. And the sequence of inserting and erasing should not matter when I use iterator.
The code is here
class LRUCache{
public:
map<int, list<pair<int,int>>::iterator> mKey;
list<pair<int,int>> lCache;
int cap;
LRUCache(int capacity) {
cap = capacity;
}
int get(int key) {
auto iter = mKey.find(key);
if(iter != mKey.end()) {
int value = (iter->second)->second;
//**the sequence of next two lines can not be changed!***
lCache.erase(iter->second);
mKey[key] = lCache.insert(lCache.begin(), make_pair(key,value));
return value;
}
return -1;
}
void set(int key, int value) {
auto iter = mKey.find(key);
if(iter == mKey.end()) {
if(lCache.size() < cap) {
mKey[key] = lCache.insert(lCache.begin(), make_pair(key,value));
}
else{
mKey[key] = lCache.insert(lCache.begin(), make_pair(key,value));
mKey.erase(lCache.back().first);
lCache.pop_back();
}
}
else {
lCache.erase(iter->second);
mKey[key] = lCache.insert(lCache.begin(), make_pair(key,value));
}
}
};
It's not quite clear what you are asking. If your question is why these two lines can't be reordered:
//**the sequence of next two lines can not be changed!***
lCache.erase(iter->second);
mKey[key] = lCache.insert(lCache.begin(), make_pair(key,value));
then that's simple. iter points to the same node as mKey[key], so the assignment actually changes the value of iter->second. If the assignment would happen first, then iter->second would point to the freshly inserted list node, not the previously existing one.
I am trying to insert pointers into a list but every time I try to print the list, or check whats in the list it says its empty. This means that my insertion is incorrect, but I don't understand why, my following classes are:
namespace {
template <typename T>
pair < node<T>*, bool> addElement (const T& elem, btree<T>* bt) {
class list < node<T>* >::iterator itr = bt->level().begin();
if (bt->level().empty()) {
node <T>*n = new node<T>(elem, bt->max());
cout << n->getItem() << endl;
bt->addElem(itr, n);
return make_pair(n, true);
}
for (; itr != bt->level().end(); ++itr) {
if (elem < (*itr)->getItem()) {
node <T>* n = new node<T> (elem, bt->max());
(*itr)->previous()->addNext(n);
n->addPrev((*itr)->previous());
n->addNext(*itr);
(*itr)->addPrev(n);
bt->addElem(itr, n);
return make_pair(n, true);
} else if (elem == (*itr)->getItem()) return make_pair(*itr, false);
}
// other stuff + return statement
}
addElem does the following:
void addElem (std::_List_iterator<node<T>*>& itr, node <T>* n) {
neighbours.insert(itr, n);
if (neighbours.empty()) cout << "wa?";
}
where btree class consists of:
size_t maxNodeElems;
list < node<T>*> neighbours;
the other things like addPrev() and previous() are just getters and setters. Anyways, I ran a test file on it that pretty much constructs a btree, and calls an insert function which directly calls this addElement function. But whenever I try to print the list inside the btree, it says its empty and seg faults. I don't understand why it's not storing.
Any help would be appreciated!
NOTE: the "Wa?" keeps printing
It seems you are trying to keep a sorted list, that's why you have:
// find the place to insert elem
for (; itr != bt->level().end(); ++itr) {
if (elem < (*itr)->getItem()) {
// insert...
}
}
But if the list is empty (initial state), itr will be probably equal bt->level().end() in the first place, so you will never insert anything...
You also should consider the case when you are trying to insert element for which (elem < (*itr)->getItem()) is always false (the new greatest element in the list). You need to handle that case as well.