Below I have a implementation of Linked List.
While there may be more errors because I have not yet finished, let's focus at the four functions: pop_front, push_front, push_back, pop_back.
Everything seems working, until I try to implement push_back. The testing code throws segmentation fault. I don't have idea why, because like it's pretty symmetric operation to the push_front but just at the another end of the list. And the push_front just works.
#include <iostream>
#ifndef LINKED_LIST_H
#define LINKED_LIST_H
#include "AbstractList.hxx"
template <class T>
class LinkedList : public AbstractList<T>
{
public:
struct Node
{
T value;
Node *prev;
Node *next;
};
LinkedList()
{
guard = new Node;
guard->prev = nullptr;
guard->next = nullptr;
}
// copy,move construction and assignment are not relevant to the question
~LinkedList()
{
clear();
}
template <class U>
void push_front(U &&x)
{
Node *fresh = new Node;
fresh->value = std::forward<U>(x);
fresh->prev = nullptr;
fresh->next = guard->next;
guard->next = fresh;
if (empty())
{
guard->prev = fresh;
}
}
T pop_front()
{
if (empty())
throw std::out_of_range("List is empty");
Node *toDelete = guard->next;
int temp = toDelete->value;
guard->next = guard->next->next;
delete toDelete;
return temp;
}
template <class U>
void push_back(U &&x)
{
Node *fresh = new Node;
fresh->value = std::forward<U>(x);
fresh->next = nullptr;
guard->prev = fresh;
if (empty())
{
guard->next = fresh;
}
}
/*
T pop_back()
{
if (empty())
throw std::out_of_range("List is empty");
Node *toDelete = guard->prev;
int temp = toDelete->value;
guard->prev = guard->prev->prev;
delete toDelete;
return temp;
}
*/
struct Iterator
{
public:
Node *ptr;
Iterator(Node *ptr_) : ptr(ptr_){};
Iterator &operator++()
{
ptr = ptr->next;
return *this;
}
Iterator &operator++(int)
{
Iterator *temp = this;
ptr = ptr->next;
return *temp;
}
Iterator &operator--()
{
ptr = ptr->prev;
return *this;
}
Iterator &operator--(int)
{
Iterator *temp = this;
ptr = ptr->prev;
return *temp;
}
bool operator==(const Iterator &b)
{
return ptr == b.ptr;
}
bool operator!=(const Iterator &b)
{
return !operator==(b);
}
T &operator*()
{
return ptr->value;
}
};
Iterator find(const T &x)
{
Iterator point = begin();
while (point != end())
{
if (*point == x)
return point;
point++;
}
// or end()?
return nullptr;
}
// usuwa cala liste
void clear()
{
Node *current = guard->next;
while (current != nullptr)
{
Node *next = current->next;
delete current;
current = next;
}
guard->prev = nullptr;
guard->next = nullptr;
}
Iterator erase(Iterator x)
{
Iterator point = begin();
while (point != end())
{
if (point == x)
{
for (point; point != end(); point++)
{
point = ++x;
}
return ++x;
}
point++;
}
return nullptr;
}
template <class U>
Iterator insert(Iterator it, U &&x)
{
for (Iterator point = it; point != end(); point++)
{
(point + 1) = point;
}
it = std::forward<U>(x);
}
int remove(const T &x)
{
int count = 0;
Iterator point = begin();
Iterator last = end();
while (point != last)
{
Iterator next = point;
++next;
if (*point == x)
{
erase(point);
count++;
}
point = next;
}
return count;
}
int size()
{
Iterator point = begin();
int count = 0;
while (point != end())
{
count++;
point++;
}
return count;
}
bool empty()
{
return guard->next == nullptr;
}
Iterator begin() const
{
return Iterator(guard->next);
}
Iterator end() const
{
return Iterator(guard->prev);
}
private:
// head = next, prev = tail
Node *guard;
};
#endif
Testing code:
#include <iostream>
#include "./LinkedList.hxx"
int main(int argc, char *argv[])
{
LinkedList<int> l1;
std::cout << l1.size() << std::endl;
l1.push_front(1);
l1.push_front(2);
l1.push_front(3);
for (auto itr = l1.begin(); itr != l1.end(); itr++)
std::cout << *itr << std::endl;
std::cout << "fpops: " << l1.pop_front() << std::endl;
std::cout << "fpops: " << l1.pop_front() << std::endl;
std::cout << "if empty " << l1.empty() << std::endl;
std::cout << "fpops: " << l1.pop_front() << std::endl;
for (auto itr = l1.begin(); itr != l1.end(); itr++)
std::cout << *itr << std::endl;
std::cout << l1.size() << std::endl;
std::cout << "if empty " << l1.empty() << std::endl;
l1.push_back(1);
std::cout << l1.size() << std::endl;
}
Related
I am currently doing an assignment to learn about doubly linked lists and iterators. I am making an insert() method that takes in an iterator type, as well as the data to add into the list. However, I'm getting an exception on the line I marked in the insert() method, saying:
Exception thrown: read access violation. iter.node was 0xFFFFFFFFFFFFFFEF
I'm not sure if this is a problem with the iterator or the linked list:
#include <cstdlib>
#include <iostream>
using namespace std;
class List {
struct Node {
int data;
Node* next = nullptr;;
Node* prev = nullptr;
};
friend ostream& operator<<(ostream& os, const List& rhs);
public:
class iterator {
friend class List;
Node* node = nullptr;
public:
iterator(Node* node) : node(node) {}
iterator& operator++() {
node = node->next;
return *this;
}
iterator& operator--() {
node = node->prev;
return *this;
}
bool operator==(const iterator& rhs) {
if (node != rhs.node) {
return false;
}
return true;
}
bool operator!=(const iterator& rhs) {
return !(*this == rhs);
}
int operator*() const {
return node->data;
}
iterator& operator->() {
return *this;
}
};
List() {
header = new Node();
trailer = new Node();
header->next = trailer;
header->prev = nullptr;
trailer->prev = header;
trailer->next = nullptr;
}
void push_back(int data) {
Node* newNode = new Node();
newNode->data = data;
newNode->prev = trailer->prev;
newNode->prev->next = newNode;
newNode->next = trailer;
trailer->prev = newNode;
}
void pop_back() {
Node* tempNode = trailer->prev->prev;
tempNode->next = trailer;
trailer->prev = tempNode;
}
void push_front(int data) {
Node* newNode = new Node();
newNode->data = data;
header->next->prev = newNode;
newNode->next = header->next;
newNode->prev = header;
header->next = newNode;
}
void pop_front() {
Node* tempNode = header->next->next;
tempNode->prev = header;
header->next = tempNode;
}
int& front() {
if (header->next == trailer) {
cerr << "List is empty" << endl;
}
return header->next->data;
}
int& back() {
if (trailer->prev == header) {
cerr << "List is empty" << endl;
}
return trailer->prev->data;
}
int front() const {
if (header->next == trailer) {
cerr << "List is empty" << endl;
}
return header->next->data;
}
int back() const {
if (trailer->prev == header) {
cerr << "List is empty" << endl;
}
return trailer->prev->data;
}
int size() const {
int count = 0;
for (iterator i = begin(); i != end(); ++i) {
++count;
}
return count;
}
int& operator[](int index) {
int ind = 0;
for (iterator i = begin(); i != end(); ++i) {
if (ind == index) {
return i.node->data;
}
++ind;
}
}
int operator[](int index) const {
int ind = 0;
for (iterator i = begin(); i != end(); ++i) {
if (ind == index) {
return i.node->data;
}
++ind;
}
}
iterator& begin() {
iterator iter(header->next);
return iter;
}
iterator& end() {
iterator iter(trailer);
return iter;
}
iterator begin() const {
iterator iter(header->next);
return iter;
}
iterator end() const {
iterator iter(trailer);
return iter;
}
iterator& insert(iterator& iter, int data) {
Node* newNode = new Node();
newNode->data = data;
iter.node->prev->next = newNode; //exception is thrown on this line
newNode->prev = iter.node->prev;
newNode->next = iter.node;
iter.node->prev = newNode;
iterator newIter(newNode);
return newIter;
}
void clear() {
Node* iter = header->next;
Node* next;
while (iter != trailer) {
next = iter->next;
delete iter;
iter = nullptr;
iter = next;
}
header->next = trailer;
trailer->prev = header;
}
iterator& erase(iterator& iter) {
for (iterator i = ++begin(); i != end(); ++i) {
if (i == iter) {
Node* tempNode = i.node;
tempNode->prev->next = tempNode->next;
tempNode->next->prev = tempNode->prev;
delete tempNode;
tempNode = nullptr;
return ++i;
}
}
}
public:
private:
Node* header;
Node* trailer;
};
ostream& operator<<(ostream& os, const List& rhs) {
for (int i : rhs) {
os << i << " ";
}
os << endl;
return os;
}
void printListInfo(const List& myList) {
cout << "size: " << myList.size()
<< ", front: " << myList.front()
<< ", back(): " << myList.back()
<< ", list: " << myList << endl;
}
int main() {
List myList;
for (int i = 0; i < 10; ++i) myList.insert(myList.end(), i * i);
printListInfo(myList);
myList.clear();
for (int i = 0; i < 10; ++i) myList.insert(myList.begin(), i * i);
printListInfo(myList);
}
iterator& iterator::begin() and iterator& iterator::end() are returning references to local variables. Since it's these functions that are being passed to List::insert(...) in main via myList.insert(myList.begin(), ..) etc, the insertion function is always going to be operating on an invalid iterator.
Similarly, iterator::insert has a similar issue in which it is returning a local variable by reference:
iterator& insert(...)
{
...
iterator newIter(newNode);
return newIter;
}
Once these functions exit, the local variables are destroyed and so those references that were returned are left dangling - pointing at memory where an object used to be.
A quick fix would be to make sure that those new iterators are not local to that function and are instead initialized on the heap e.g.
iterator& begin() {
iterator* iter = new iterator(header->next);
return *iter;
}
iterator& end() {
iterator* iter = new iterator(trailer);
return *iter;
}
However, this also means that you would have to introduce a way to keep track of those iterators so that you can free the memory once an iterator is no longer needed, perhaps by storing the iterators as a member variable of List instead of storing the begin/end Nodes of List and therefore only accessing the Nodes via the iterators.
I found this due to compiling with warnings on, which is highly recommended!
In member function 'List::iterator& List::begin()':
<source>:151:16: warning: reference to local variable 'iter' returned [-Wreturn-local-addr]
151 | return iter;
| ^~~~
<source>:150:18: note: declared here
150 | iterator iter(header->next);
New to c++ and I'm having trouble implementing an Iterator class in my LinkedList. I have a Iterator class defined in the private section of my LinkedList class as follows:
cs_linked_list.h
#ifndef LINKED_LIST_H_
#define LINKED_LIST_H_
#include <initializer_list>
#include <iostream>
namespace cs {
template <typename T>
class LinkedList {
struct Node; // forward declaration for our private Node type
public:
/**Constructs an empty list.**/
LinkedList(){
head_ = nullptr;
}
/**
* #brief Constructs a list from a range.
*
*/
template <class InputIterator>
LinkedList(InputIterator first, InputIterator last) {
for (; first != last; ++first) this->push_back(*first);
}
/** Constructs a list with a copy of each of the elements in `init_list`, in the same order. */
LinkedList(std::initializer_list<T> init_list) {
this->operator=(init_list); // call initializer list assignment
}
/**Constructs a container with a copy of each of the elements in another, in the same order**/
LinkedList(const LinkedList<T>& another){
//TODO
this->operator=(another);
}
/** Destroys each of the contained elements, and deallocates all memory allocated by this list. */
~LinkedList() {
while (this->head_) {
Node* old_head = this->head_;
this->head_ = old_head->next;
delete old_head;
}
}
/**Returns the number of elements in this list.Node *head**/
size_t size() const{ //DONE
//TODO
Node *temp = head_;
size_t len = 0;
while (temp != nullptr){
len++;
temp = temp->next;
}
return len;
}
/**Returns whether the list container is empty (that is, whether its size is 0). **/
bool empty() const{ //DONE
if(size_ == 0){
return true;
} else{
return false;
}
}
/** Appends a copy of `val` to this list. */
void push_back(const T& val) {
Node* new_node = new Node{val};
if (this->size_ == 0) {
this->head_ = this->tail_ = new_node;
} else {
this->tail_->next = new_node;
new_node->prev = this->tail_;
this->tail_ = new_node;
}
++this->size_;
}
/** Prepends a copy of `val` to this list. */
void push_front(const T& val) {
Node* new_node = new Node{val};
if (this->size_ == 0) {
this->head_ = this->tail_ = new_node;
} else {
new_node->next = this->head_;
this->head_->prev = new_node;
this->head_ = new_node;
}
++this->size_;
}
/**Returns a reference to the value in the first element in this list.**/
T& front() const{
return head_->data;
}
/**Returns a reference to the value in the last element in this list. **/
T& back() const{
return tail_->data;
}
/**Deletes the first value in this list. **/
void pop_front(){
Node *temp = head_;
if(empty()){
return;
}
if(temp == tail_){
return;
}
head_ = head_->next;
if (head_ != nullptr) {
head_->prev = nullptr;
} else {
tail_ = nullptr;
}
delete temp;
}
/**Deletes the last value in this list**/
void pop_back(){
if(empty()){
return;
}
if(head_ == tail_){
return;
}
Node *temp = head_;
while(temp->next->next != nullptr){
temp = temp->next;
}
tail_ = temp;
delete tail_->next;
tail_->next = nullptr;
size_--;
}
/**resizes the list so that it contains n elements.**/
void resize(std::size_t n){
//TODO
for (size_t i = 0; i < n; i++){
push_back('\0');
}
}
/**resizes the list so that it contains n elements**/
void resize(std::size_t n, const T &fill_value){
//TODO
for (size_t i = 0; i < n; i++) {
push_back(fill_value);
}
}
/**Removes from the container all the elements that compare equal to val. **/
void remove(const T &val){
//TODO
Node *p1 = head_;
Node *p2 = nullptr;
if(p1 != nullptr && p1->data == val){
head_ = p1->next;
delete p1;
} else {
while (p1 != nullptr && p1->data != val){
p2 = p1;
p1 = p1->next;
}
if (p1 == nullptr) {
return;
}
p2->next = p1->next;
delete p1;
}
}
/**Removes duplicate values from this list**/
void unique(){
//TODO
Node *temp = head_;
while (temp != nullptr && temp->next != nullptr) {
Node *temp2 = temp;
while (temp2->next != nullptr) {
if (temp->data == temp2->next->data) {
Node *temp3 = temp2->next;
temp2->next = temp2->next->next;
delete temp3;
} else {
temp2 = temp2->next;
}
}
temp = temp->next;
}
}
/**Deletes all values in this list.**/
void clear(){
//TODO
while (head_ != nullptr){
Node *temp = head_;
head_ = head_->next;
delete temp;
}
tail_ = nullptr;
size_ = 0;
}
/**Reverses the order of the elements in this list.**/
void reverse(){
//TODO
Node *p1 = head_;
Node *p2 = nullptr;
while (p1 != nullptr) {
Node *temp = p1->next;
p1->next = p2;
p2 = p1;
p1 = temp;
}
head_ = p2;
}
/** Replaces the contents of this list with a copy of each element in `init_list`. */
LinkedList& operator=(std::initializer_list<T> init_list) {
this->size_ = 0;
for (auto&& val : init_list)
this->push_back(val);
return *this;
}
/**Replaces the contents of this list with a copy of each element in another, in the same order.**/
LinkedList& operator=(const LinkedList& another){
//TODO
if (this != &another) {
this->clear();
Node *temp = another.head_;
this->size_ = 0;
while (temp) {
this->push_back(temp->data);
temp = temp->next;
}
}
return *this;
}
/**Compares this list with another for equality.**/
bool operator==(const LinkedList &another){ //DONE
//TODO
auto comp = head_;
auto comp2 = another.head_;
while(comp != nullptr){
if(comp != comp2){
return false;
}
comp = comp->next;
comp2 = comp2->next;
}
return true;
}
/**Compares this list with another for inequality. **/
bool operator!=(const LinkedList &another){ //DONE
//TODO
auto comp = head_;
auto comp2 = another.head_;
while(comp != nullptr){
if(comp != comp2){
return true;
}
comp = comp->next;
comp2 = comp2->next;
}
return false;
}
/** Inserts this list into an ostream, with the format `[element1, element2, element3, ...]` */
friend std::ostream& operator<<(std::ostream& out, const LinkedList& list) {
out << '[';
for (Node* cur = list.head_; cur; cur = cur->next) {
out << cur->data;
if (cur->next)
out << ", ";
}
out << ']';
return out;
}private:
struct Node {
T data;
Node* next = nullptr;
Node* prev = nullptr;
};
Node* head_ = nullptr;
Node* tail_ = nullptr;
std::size_t size_ = 0;
class Iterator {
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = T;
using difference_type = int;
using pointer = T*;
using reference = T&;
// Default constructor
Iterator() {
//TODO
n = nullptr;
}
// Copy constructor
Iterator(const Iterator& other) {
//TODO
this->operator=(other);
}
//Destructor if needed
~Iterator() {
//TODO
while (this->list.head_){
Node *old_head = this->list.head_;
this->list.head_ = old_head->next;
delete old_head;
}
}
// Copy assign
Iterator& operator=(const Iterator& that) {
//TODO
if(this != &that){
this->list.clear();
Node *temp = that.list.head_;
this->list.size_ = 0;
while (temp){
this->list.push_back(temp->data);
temp = temp->next;
}
}
return *this;
}
// Prefix increment
Iterator& operator++() {
//TODO
this->n = this->n->next;
return *this;
}
// Postfix increment
Iterator operator++(int) {
Iterator tmp(*this);
this->operator++();
return tmp;
}
// Prefix decrement
Iterator& operator--() {
//TODO
this->n = this->n->prev;
return *this;
}
// Postfix decrement
Iterator operator--(int) {
Iterator tmp(*this);
this->operator--();
return tmp;
}
// Inequality
bool operator!=(Iterator that) const {
return !(this->operator==(that));
}
// Equality
bool operator==(Iterator that) const {
//TODO
auto temp = list.head_;
auto temp2 = that.list.head_;
while(temp != nullptr){
if(*temp != *temp2){
return false;
}
temp = temp->next;
temp2 = temp2->next;
}
return true;
}
// lvalue dereference
T& operator*() const {
//TODO
return this->n->data;
}
// referring
Iterator* operator->() const {
return this;
}
Iterator begin(){
//TODO
return Iterator(list.head_->next);
}
Iterator end(){
//TODO
return Iterator(list.tail_);
}
private:
Node *n;
LinkedList<T> list;
};
};
} // namespace cs
#endif // LINKED_LIST_H_
Main:
#include "cs_linked_list.h"
#include <iostream>
int main() {
/***TESTING***/
cs::LinkedList<int> list;
// Add few items to the end of LinkedList
list.push_back(1);
list.push_back(2);
list.push_back(3);
std::cout << "Traversing LinkedList through Iterator" << std::endl;
for ( cs::LinkedList<int>::Iterator iter = list.begin();iter != list.end(); iter++) {
std::cout << *iter << " ";
}
std::cout << std::endl;
return 0;
}
Since my Iterator class is private I can't seem to implement my Begin() and End() functions. Should I Make it public or am I missing one crucial step. Instructions say to define a Iterator class in the private section of my LinkedList class.
I'm curious about how to implement the iterator pattern the way STL does in a stack ADT.
#include <iostream>
#include <vector>
int main() {
std::vector<char> v = { 'a', 'b', 'c', 'd', 'e', 'f'};
std::vector<char>::iterator it = v.begin();
while(it != v.end()) {
std::cout << *it << "->";
++it;
}
std::cout << "\n";
return 0;
}
Output
a->b->c->d->e->f->
so far I have implemented the following code
#include <iostream>
#include <memory>
template<class T>class Node {
private:
T data = 0;
std::shared_ptr<Node<T>> next_node = nullptr;
public:
Node(T data = 0, std::shared_ptr<Node<T>> next_node = nullptr)
: data(data), next_node(next_node)
{
std::cout << "created node[" << data << "]\n";
}
~Node() {
std::cout << "deleted node[" << data << "]\n";
}
// getters and setters
T getData() const {
return this->data;
}
std::shared_ptr<Node<T>> getNextNode() const {
return this->next_node;
}
void setData(T value) {
this->data = value;
}
void setNextNode(std::shared_ptr<Node<T>> node) {
this->next_node = node;
}
};
template<class T>std::ostream& operator<<(std::ostream& o, const std::shared_ptr<Node<T>> node) {
return o << "node["<< node->getData() <<"]-> ";
}
template<class T>class Stack {
private:
std::shared_ptr<Node<T>> top = nullptr;
public:
Stack()
: top(nullptr)
{ /* empty */ }
~Stack() { /* empty */ }
void push(T value) {
if(!top) {
top = std::shared_ptr<Node<T>> (new Node<T>(value));
} else {
top = std::shared_ptr<Node<T>> (new Node<T>(value, top));
}
}
void display() {
if(!top) {
std::cout << "display::The stack is empty.\n";
} else {
std::shared_ptr<Node<T>> p = top;
while(p) {
std::cout << p;
p = p->getNextNode();
}
std::cout << "\n";
}
}
class Iterator {
private:
std::shared_ptr<Node<T>> node;
public:
Iterator(std::shared_ptr<Node<T>> node)
: node(node)
{ /* empty */ }
bool hasMore() {
return node->getNextNode() != nullptr;
}
Iterator getNext() {
return Iterator(node->getNextNode());
}
int getData() {
return node->getData();
}
};
Iterator begin() const {
return Iterator(top);
}
Iterator getIterator() {
Iterator it = Iterator(top);
return it;
}
};
int main() {
Stack<char> stack;
for(char i = 'a'; i < 'f'; ++i) {
stack.push(i);
}
Stack<char>::Iterator it = stack.begin();
while(it.hasMore()) {
std::cout << it.getData() << "->";
it = it.getNext();
}
std::cout << "\n";
return 0;
}
Output:
created node[a]
created node[b]
created node[c]
created node[d]
created node[e]
101->100->99->98->
deleted node[e]
deleted node[d]
deleted node[c]
deleted node[b]
deleted node[a]
My question is how to implement nested template detection for the Iterator class, as you can see the expected output is a char type and I am getting integers.
Can someone help me understand how this is implemented in the STL and how it could be implemented in an ADT?
thanks!!!
Thanks for the comments I be able to fix the problem I was returning the wrong data type on int getData() { return node->getData(); } I just change the int type for T type and everithing works ok!
also change the hasMore method for bool hasMore() { return node != nullptr; }
#include <iostream>
#include <memory>
template<class T>class Node {
private:
T data = 0;
std::shared_ptr<Node<T>> next_node = nullptr;
public:
Node(T data = 0, std::shared_ptr<Node<T>> next_node = nullptr)
: data(data), next_node(next_node)
{
std::cout << "created node[" << data << "]\n";
}
~Node() {
std::cout << "deleted node[" << data << "]\n";
}
// getters and setters
T getData() const {
return this->data;
}
std::shared_ptr<Node<T>> getNextNode() const {
return this->next_node;
}
void setData(T value) {
this->data = value;
}
void setNextNode(std::shared_ptr<Node<T>> node) {
this->next_node = node;
}
};
template<class T>std::ostream& operator<<(std::ostream& o, const std::shared_ptr<Node<T>> node) {
return o << "node["<< node->getData() <<"]-> ";
}
template<class T>class Stack {
private:
std::shared_ptr<Node<T>> top = nullptr;
public:
Stack()
: top(nullptr)
{ /* empty */ }
~Stack() { /* empty */ }
void push(T value) {
if(!top) {
top = std::shared_ptr<Node<T>> (new Node<T>(value));
} else {
top = std::shared_ptr<Node<T>> (new Node<T>(value, top));
}
}
void display() {
if(!top) {
std::cout << "display::The stack is empty.\n";
} else {
std::shared_ptr<Node<T>> p = top;
while(p) {
std::cout << p;
p = p->getNextNode();
}
std::cout << "\n";
}
}
class Iterator {
private:
std::shared_ptr<Node<T>> node;
public:
Iterator(std::shared_ptr<Node<T>> node)
: node(node)
{ /* empty */ }
bool hasMore() {
return node != nullptr;
}
Iterator getNext() {
return Iterator(node->getNextNode());
}
T getData() {
return node->getData();
}
};
Iterator begin() const {
return Iterator(top);
}
Iterator getIterator() {
Iterator it = Iterator(top);
return it;
}
};
int main() {
Stack<char> stack;
for(char i = 'a'; i < 'f'; ++i) {
stack.push(i);
}
Stack<char>::Iterator it = stack.begin();
while(it.hasMore()) {
std::cout << it.getData() << "->";
it = it.getNext();
}
std::cout << "\n";
return 0;
}
Output
created node[a]
created node[b]
created node[c]
created node[d]
created node[e]
e->d->c->b->a->
deleted node[e]
deleted node[d]
deleted node[c]
deleted node[b]
deleted node[a]
I have spent some time recently designing an iterator for the AVL Tree (right now it just has the inserting mechanics though; haven't implemented tree balancing).
I wanted to test out the iterator, so I checked how to make it online and settled on making it by having a stack holding the tree nodes (e.g. in normal iteration stack would contain all nodes left of this->top node).
This is how the iteration is supposed to work:
for (auto it = tree.iterator(); it.hasNext(); it.next())
{
// process
}
However, VS changes (disables) my Iterator(const Iterator& it) and Iterator(Iterator&& it) constructors and then the iteration fails because the stack is always empty.
After setting Iterator() = delete;, I run into the issue of stack having an unusually large size with invisible parameters.
If extra information is needed, feel free to ask. I think that it's best if I just paste the relevant code because I do not understand this behaviour and do not know what details I should say:
avlTree<Key, Info>::iterator:
class Iterator
{
private:
std::vector<Node*> stack;
bool reverse;
Node* ptr;
std::vector<Node*> makeStack(Node* start, long height)
{
std::vector<Node*> newStack;
newStack.reserve(height);
while (start != nullptr)
{
newStack.push_back(start);
if (reverse)
start = start->right;
else
start = start->left;
}
return newStack;
}
Iterator(Node* start, long height, bool reverse = false) : reverse(reverse), ptr(nullptr)
{
stack = makeStack(start, height);
}
friend class avlTree;
public:
Iterator(Iterator&& iterator)
{
stack = move(iterator.stack);
ptr = nullptr;
}
Iterator(const Iterator& iterator)
{
stack = iterator.stack;
ptr = nullptr;
}
//Iterator() = delete;
bool hasNext()
{
return stack.size() > 0;
}
void next()
{
if (!stack.size()) throw "Empty iterator stack";
if (ptr == stack[stack.size() - 1])
{
stack.pop_back();
if (reverse) // fill the stack with the subsequent nodes (reverse or normal direction)
{
Node* start = ptr->left;
while (start != nullptr)
{
stack.push_back(start);
start = start->right;
}
}
else
{
Node* start = ptr->right;
while (start != nullptr)
{
stack.push_back(start);
start = start->left;
}
}
}
if (stack.size() > 0)
ptr = stack[stack.size() - 1];
}
const Key& getKey()
{
if (!ptr) throw "ptr is nullptr";
else return ptr->key;
}
Info& getInfo()
{
if (!ptr) throw "ptr is nullptr";
else return ptr->info;
}
};
main:
avlTree<char, int> tester;
for (char i = 'g'; i <= 'z'; ++i)
tester.insert(i);
for (char i = 'a'; i < 'g'; ++i)
tester.insert(i);
for (auto it = tester.iterator(); it.hasNext(); it.next())
{
std::cout << it.getKey() << " ";
}
Screenshot of the code & message I get while debugging: http://prntscr.com/qi79zd
How do I fix the issue and make the iteration work?
EDIT:
Complete code:
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <fstream>
#include <chrono>
#include <iterator>
#include <functional>
//#include <ctime>
template<typename T>
void swap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
template<typename Key, typename Info>
class avlTree
{
private:
struct Node
{
const Key key;
Info info;
Node* left;
Node* right;
long leftHeight, rightHeight;
Node(const Key& key, Info&& info = Info(), Node* left = nullptr, Node* right = nullptr)
: key(key), info(info), left(left), right(right), leftHeight(1), rightHeight(1) {}
Node& operator()(Node* nleft, Node* nright)
{
left = nleft;
right = nright;
return *this;
}
Node& operator()(long left, long right)
{
leftHeight = left;
rightHeight = right;
}
};
Node* top;
long length;
public:
class Iterator
{
private:
std::vector<Node*> stack;
bool reverse;
Node* ptr;
std::vector<Node*> makeStack(Node* start, long height)
{
std::vector<Node*> newStack;
newStack.reserve(height);
while (start != nullptr)
{
newStack.push_back(start);
if (reverse)
start = start->right;
else
start = start->left;
}
return newStack;
}
Iterator(Node* start, long height, bool reverse = false) : reverse(reverse), ptr(nullptr)
{
stack = makeStack(start, height);
}
friend class avlTree;
public:
Iterator(Iterator&& iterator)
{
stack = move(iterator.stack);
ptr = nullptr;
}
Iterator(const Iterator& iterator)
{
stack = iterator.stack;
ptr = nullptr;
}
bool hasNext()
{
return stack.size() > 0;
}
void next()
{
if (!stack.size()) throw "Empty iterator stack";
//stack.insert(stack.end(), vector.begin(), vector.end());
if (ptr == stack[stack.size() - 1])
{
stack.pop_back();
if (reverse)
{
Node* start = ptr->left;
while (start != nullptr)
{
stack.push_back(start);
start = start->right;
}
}
else
{
Node* start = ptr->right;
while (start != nullptr)
{
stack.push_back(start);
start = start->left;
}
}
}
if (stack.size() > 0)
ptr = stack[stack.size() - 1];
}
const Key& getKey()
{
if (!ptr) throw "ptr is nullptr";
else return ptr->key;
}
Info& getInfo()
{
if (!ptr) throw "ptr is nullptr";
else return ptr->info;
}
};
avlTree()
{
this->top = nullptr;
this->length = 0;
}
~avlTree()
{
recursiveDelete(top);
length = 0;
}
void printAsc()
{
for (auto it = iterator(); it.hasNext(); it.next())
{
std::cout << it.getKey() << " " << it.getInfo() << "\n";
}
}
void printDesc()
{
recDesc(top);
}
void printTop()
{
if (top) // != nullptr
{
std::cout << ".." << top->key << std::endl;
if (top->left)
std::cout << "." << top->left->key << "..";
else std::cout << ".0..";
if (top->right)
std::cout << top->right->key << std::endl;
else std::cout << "0" << std::endl;
}
}
void insert(const Key& key);
long height()
{
return !top ? 0 : top->leftHeight > top->rightHeight ? top->leftHeight : top->rightHeight;
}
private:
void recDesc(Node* parent);
void recursiveDelete(Node* parent);
void insertRecursive(Node* parent, const Key& key, int& depth);
// void rightRotation(Node* top, Node* parent = nullptr);
public:
Iterator iterator()
{
return Iterator(top, height());
}
};
std::vector<std::string> readFile(bool toDarwin = true);
/****************************************************************************/
int main()
{
// auto start = std::chrono::system_clock::now();
avlTree<std::string, int> counter;
avlTree<char, int> tester;
for (char i = 'g'; i <= 'z'; ++i)
tester.insert(i);
for (char i = 'a'; i < 'g'; ++i)
tester.insert(i);
for (auto it = tester.iterator(); it.hasNext(); it.next())
{
std::cout << it.getKey() << " ";
}
return 0;
}
/****************************************************************************/
template<typename Key, typename Info>
void avlTree<Key, Info>::recDesc(Node* parent)
{
if (parent->left != nullptr)
recAsc(parent->left);
std::cout << parent->key;
if (parent->right != nullptr)
recAsc(parent->left);
}
template<typename Key, typename Info>
void avlTree<Key, Info>::recursiveDelete(Node* parent)
{
if (!parent) return;
if (parent->left != nullptr)
recursiveDelete(parent->left);
if (parent->right != nullptr)
recursiveDelete(parent->right);
delete parent;
}
template<typename Key, typename Info>
void avlTree<Key, Info>::insertRecursive(Node* parent, const Key& key, int& depth)
{
if (parent->key == key)
++(parent->info);
else if (parent->key > key)
{
if (parent->left == nullptr)
{
parent->left = new Node(key);
++(parent->left->info);
++length;
depth = 1;
// (* parent->left)(depth, depth)
}
else
{
insertRecursive(parent->left, key, depth);
++depth;
parent->leftHeight = depth;
}
}
else if (parent->key < key)
{
if (parent->right == nullptr)
{
parent->right = new Node(key);
++(parent->right->info);
++length;
depth = 1;
// (* parent->right)(depth, depth)
}
else
{
insertRecursive(parent->right, key, depth);
++depth;
parent->rightHeight = depth;
}
}
}
template<typename Key, typename Info>
void avlTree<Key, Info>::insert(const Key& key)
{
int depth = 0;
if (!top)
{
top = new Node(key);
// (*top)(1, 1)
++length;
++(top->info);
}
else
{
insertRecursive(top, key, depth);
++depth;
top->key > key ? ++(top->leftHeight) : top->key < key ? ++(top->rightHeight) : NULL;
}
}
/* Irrelevant to the problem
std::vector<std::string> readFile(bool toDarwin)
{
// shrink_to_fit()
std::ifstream file;
std::string word;
std::vector<std::string> words;
words.reserve(1000000);
if (toDarwin == 1)
file.open("OnTheOriginOfSpecies.txt");
else
file.open("The_bible.txt");
while (file >> word)
{
words.push_back(word);
}
words.shrink_to_fit();
return words;
}
*/
I believe the problem is that you are not aware of RVO - return value optimization. Most compilers do so and in fact it is mandatory in C++17. What's RVO?
class A;
A func()
{
A a_infunc = {};
return a_infunc;
}
//use
A a_outsidefunc = func();
In this simple example at no point A::A(const A&) or A::A(A&&) is called. a_infunc is exactly the same variable as a_outsidefunc.
So in the for-loop:
for (auto it = tree.iterator(); it.hasNext(); it.next())
{
// process
}
There will be no calls to Iterator(const Iterator& it) or Iterator(Iterator&& it) due to RVO.
I am implementing a doubly linked List using iterators. The code works fine, except when using iterator::end() I am unable to access the last element in the list. For example the copy constructor cant access the last element(!!whenever I use for(iterator it = lst.begin(); it != lst.end();++it)). The problem looks simple but I cant get my head around it.
#pragma once
#include <iterator>
#include <initializer_list>
#include <iostream>
using namespace std;
template <typename T, class Allocator = std::allocator<T>>
class MyList {
private:
class Link {
public:
Link(const T& d, Link *n = NULL, Link *p = NULL) :next(n), prev(p), data(d) {}
~Link() { }
T data;
Link *next;
Link *prev;
};
Link *head ;
Link *tail ;
size_t s = 0; // ease things up
public:
class iterator:public std::iterator<std::bidirectional_iterator_tag, Link>
{
private:
Link *itr;
public:
iterator() :itr(nullptr) {}
iterator(Link* x) :itr(x) {}
// iterator& operator=(const iterator& i2) {itr = i2.itr;}
iterator(const iterator& i2) : itr(i2.itr) {}
iterator& operator++() {
itr=itr->next;
return *this;
}
iterator& operator--() {
itr = itr->prev;
return *this;
}
bool operator==(const iterator& rhs) {
return itr == rhs.itr;
}
bool operator!=(const iterator& rhs) {
return itr != rhs.itr;
}
T& operator*() {
return itr->data;
}
Link* getLink()const{
return itr;
}
};
MyList() {
head = nullptr;
tail= head;
s=0;
}
MyList(std::initializer_list<T> l)
:MyList(){
for(const auto& i : l){
push_back(i);}
}
//copy consructor
MyList( const MyList<T> &lst)
:MyList(){
for(iterator it = lst.begin(); it != lst.end(); ++it){
push_back(it.getLink()->data);
}
}
MyList& operator=(std::initializer_list<T> &lst) {
//clear any data before adding new one
while(head){
Link *tmp = head;
head = head->next;
delete tmp;
}
head = nullptr;
tail = nullptr;
s = 0;
for(auto i: lst){
push_back(i);
}
}
MyList& operator=(MyList<T> &lst) {
while(head){
Link *tmp = head;
head = head->next;
delete tmp;
}
head = nullptr;
tail = nullptr;
s = 0;
for(iterator it = lst.begin(); it != lst.end();++it) {push_back(it.getLink()->data);}
}
~MyList() {
Link* temp = head;
while (temp != nullptr)
{
temp = temp->next;
delete(head);
head = temp;
}
}
iterator begin() const{
iterator i(this->head);
return i;
}
iterator end() const{
iterator return{tail};
}
void push_back(const T& t) {
Link* newnode = new Link(t);
if (empty()) {
head = newnode;
tail = head;
}
else {
tail->next = newnode;
newnode->prev = tail;
tail = newnode;
}
s++;
}
std::size_t size() const {
return s;
}
bool empty() const {
return !(this->size());
}
};
This is the main.cpp I am testing my code, you can see in () when is the code working and when its failing.
//test default constructor(works!!!)
std::cout << "Testing default constructor"<< std::endl;
MyList<int> a{};
std::cout << "a should be empty: " << (a.empty() ? string("and it is!") : string("but it is not!")) << std::endl;
// push_back two elements(works!!!)
std::cout << "Testing push_back"<< std::endl;
a.push_back(1);
a.push_back(2);
std::cout << "a should be 1,2, and is: " << a << std::endl;
// test initializer list constructor(works!!!!)
std::cout << "Testing initializer list constructor"<< std::endl;
MyList<int> b{1, 2, 3, 4};
std::cout << "b should be 1,2,3,4, and is: " << b << std::endl;
//test copy constructor(doesnt work!! Misses the last element)
std::cout << "Testing copy constructor"<< std::endl;
MyList<int> c(b);
std::cout << "c should be " << b << " and is: " << c << std::endl;
MyList<int> ml{1,2,3,4,5,6};
// (doesnt work!! misss the last element)
for(const int& elem : ml)
std::cout << elem << std::endl;
This is because the standard convention in C++ is that end() points not to the last element, but to the next nonexsitent would-be element after the last.
http://www.cplusplus.com/reference/list/list/end/
In your implementation, iterator end() returns an actual last element which is obviously skipped in the it != lst.end() condition in the for loop:
for(iterator it = lst.begin(); it != lst.end();++it)