I'm facing this problem when I remove an element from my list.
Here my list.h:
class AwesomeList {
friend class Iteratore;
private:
class Nodo;
class SmartPointer {
public:
Nodo* punt;
SmartPointer(Nodo* n = 0): punt(n) {}
SmartPointer(const SmartPointer& ptr): punt(ptr.punt) {}
~SmartPointer() {
delete punt;
}
SmartPointer& operator=(const SmartPointer& ptr) {
if (this != &ptr) {
delete punt;
punt = ptr.punt;
}
return *this;
}
bool operator==(const SmartPointer& ptr) const {
return ptr.punt == punt;
}
bool operator!=(const SmartPointer& ptr) const {
return ptr.punt != punt;
}
Nodo* operator->() const {
return punt;
}
Nodo& operator*() const {
return *punt;
}
};
class Nodo {
public:
T* value;
SmartPointer next;
Nodo(T* t = T(), const SmartPointer& ptr = SmartPointer()): value(t), next(ptr) {}
};
SmartPointer head;
SmartPointer tail;
public:
class Iteratore{
friend class AwesomeList;
private:
AwesomeList::SmartPointer punt;
public:
bool operator==(const Iteratore& it) const {
return it.punt == punt;
}
bool operator!=(const Iteratore& it) const {
return it.punt != punt;
}
Iteratore& operator++() {
if(punt != 0) punt = punt->next;
return *this;
}
Iteratore& operator++(int) {
if(punt != 0) punt = punt->next;
return *this;
}
T* operator*() const {
if (punt != 0) return punt->value;
}
};
AwesomeList(const SmartPointer& ptr = 0): head(ptr), tail(0) {
if (head != 0) {
SmartPointer p = head;
while (p != 0)
p = p->next;
tail = p;
}
}
AwesomeList(const AwesomeList& list): head(list.head), tail(list.tail) {}
AwesomeList& operator=(const AwesomeList& list) {
head = list.head;
tail = list.tail;
}
int getSize() const {
int count = 0;
SmartPointer p = head;
while (p != 0) {
p = p->next;
count++;
}
return count;
}
bool isEmpty() const {
return getSize() == 0;
}
T* at(int pos) const {
if (pos > -1 && pos < getSize()) {
SmartPointer p = head;
while (pos--) {
p = p->next;
}
return p->value;
} else return 0;
}
void add(const T& t) {
if (head == 0) {
head = SmartPointer(new Nodo(&(const_cast<T&>(t))));
tail = head;
} else {
tail->next = SmartPointer(new Nodo(&(const_cast<T&>(t))));
tail = tail->next;
}
}
void remove(int pos) {
if (pos > -1 && pos < getSize()) {
SmartPointer newHead = head;
SmartPointer p = newHead;
head = 0;
while (pos--) {
add(*p->value);
p = p->next;
}
p = p->next;
while (p != 0) {
add(*p->value);
p = p->next;
}
}
}
void replace(int pos, T* t) {
if (pos > -1 && pos < getSize()) {
SmartPointer p = head;
while (pos--)
p = p->next;
p->value = t;
}
}
void replace(int pos, const T& t) {
if (pos > -1 && pos < getSize()) {
SmartPointer p = head;
while (pos--)
p = p->next;
T& t_obj = const_cast<T&>(t);
p->value = &t_obj;
}
}
Iteratore begin() const {
Iteratore it;
it.punt = head;
return it;
}
Iteratore end() const {
Iteratore it;
it.punt = 0;
return it;
}
T* operator[](const Iteratore& it) const {
return it.punt->value;
}
};
This are the tests I made:
AwesomeList<int> list = AwesomeList<int>();
list.add(1);
list.add(2);
list.add(3);
for (int i = 0; i < list.getSize(); i++)
qDebug() <<*(list.at(i)) <<" ";
list.remove(-1);
for (int i = 0; i < list.getSize(); i++)
qDebug() <<*(list.at(i)) <<" ";
list.remove(2);
for (int i = 0; i < list.getSize(); i++)
qDebug() <<*(list.at(i)) <<" ";
list.replace(0, 5);
qDebug() <<"Replace in posizione 0";
auto cit = list.begin();
for (; cit != list.end(); cit++)
qDebug() <<*(*cit) <<" ";
qDebug() <<"Size";
qDebug() <<list.getSize() <<endl;
This is the lines where errors appears:
AwesomeList::Nodo::~Nodo() + 16 (awesomelist.h:8)
AwesomeList::SmartPointer::~SmartPointer() + 42 (awesomelist.h:21)
AwesomeList::SmartPointer::~SmartPointer() + 21 (awesomelist.h:22)
Any help is appreciate. Thanks!
UPDATE
I solved my problem changing SmartPointer and Nodo classes like this:
SmartPointer(Nodo* n = 0): punt(n) {
if (punt) punt->references++;
}
SmartPointer(const SmartPointer& ptr): punt(ptr.punt) {
if (punt) punt->references++;
}
~SmartPointer() {
if (punt) {
punt->references--;
if (punt->references == 0) delete punt;
}
}
SmartPointer& operator=(const SmartPointer& ptr) {
if (this != &ptr) {
Nodo* n = punt;
punt = ptr.punt;
if (punt) punt->references++;
if (n) {
n->references--;
if (n->references == 0) delete n;
}
}
return *this;
}
bool operator==(const SmartPointer& ptr) const {
return ptr.punt == punt;
}
bool operator!=(const SmartPointer& ptr) const {
return ptr.punt != punt;
}
Nodo* operator->() const {
return punt;
}
Nodo& operator*() const {
return *punt;
}
};
class Nodo {
public:
T* value;
SmartPointer next;
int references;
Nodo(T* t = T(), const SmartPointer& ptr = SmartPointer()): value(t), next(ptr), references(0) {}
};
Sorry but I don't understand the ratio beneath your SmartPointer class.
It carry a pointer to a Nodo and delete it with the constructor. Good.
But, if I'm not wrong
(1) when you create a SmartPointer with copy constructor, you copy the pointer from the SmartPointer copied so you have two object with a punt with the same value; when you destroy the two objects, you call delete two times over the same pointer; this can crash the program
(2) when you call operator=, you have the same problem with the copy constructor but, moreover, you don't delete the old pointed value
By example, look at add()
head = SmartPointer(new Nodo(&(const_cast<T&>(t))));
tail = head;
You create a temporary SmartPointer object initializing it with new Nodo(&(const_cast<T&>(t))). Next you copy this temporary object in head, so both head and the temporary object are carrying the same not-NULL pointer. Now the temporary object is destroyed, so the memory pointed by punt is deleted but head (his punt) continue to point to a memory area that is deleted. Now you copy head in tail, and you have both head and tail that are pointing to the same deleted area.
Look at the else case
tail->next = SmartPointer(new Nodo(&(const_cast<T&>(t))));
tail = tail->next;
In this case, tail->next (and take in count that take point to a deleted area) receive a pointer from a temporary object that delete it. So you write in a deleted area a pointer that is immediately deleted.
I hope it is clear how much all this is dangerous.
Suggestion: redesign SmartPointer class.
p.s.: sorry for my bad English.
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 have a Doubly linked List class wherein i have a function which uses my classes iterators to loop over the collection. However in 1 specific place it runs for an extra loop and I cannot figure out why.
current merge template
template <typename T>
void DlList<T>::merge(SortedList& other) {
iterator it = back_->prev_;
iterator oit = other.begin();
while (oit.current_->next_ != NULL) {
std::cout << *it << " " << *oit << std::endl;
it.current_->next_ = oit.current_;
back_->prev_ = oit.current_;
//back_->prev_->next_ = back_;
//other.back_->prev_ = other.back_->prev_->prev_;
it++;
oit++;
}
}
It always iterates and extra time and added a null node to the list and I don't understand why.
Any insight is greatly appricicated!
Edit Added full project examples to explain intention of function
I am working on a templated data structure class which is a doubly linked list which uses sentinel nodes.
The list is sorted based on insert() and I am working on a merge function wherein the nodes of each list must be combined into this->list. The nodes must be moved and not have new ones created. and if the same value exists in both lists the other node value must come after the current node value.
I coded what I thought was a logical implementation however the output I get is not as expected and the results do not make sense to me, so if anyone could explain how i am getting the results I have it would be greatly appreciated.
Class Definition
class SortedList {
struct Node {
T data_;
Node* next_;
Node* prev_;
Node(const T& data = T{}, Node* nx = nullptr, Node* pr = nullptr) {
data_ = data;
next_ = nx;
prev_ = pr;
}
};
Node* front_;
Node* back_;
public:
class const_iterator {
friend class SortedList;
Node* current_;
const_iterator(Node* n)
{
current_ = n;
}
public:
const_iterator() {
//Set to safe state
current_ = nullptr;
}
const_iterator& operator++() {
current_ = current_->next_;
return *this;
}
const_iterator operator++(int) {
const_iterator old = *this;
current_ = current_->next_;
return old;
}
const_iterator& operator--() {
current_ = current_->prev_;
return *this;
}
const_iterator operator--(int) {
const_iterator old = *this;
current_ = current_->prev_;
return old;
}
bool operator==(const_iterator rhs) {
return (current_ == rhs.current_) ? true : false;
}
bool operator!=(const_iterator rhs) {
return !(*this == rhs);
}
bool operator>(const_iterator rhs) {
return current_->data_ > rhs->current_->data_;
}
const T& operator*()const {
return current_->data_;
}
};
class iterator :public const_iterator {
friend SortedList;
iterator(Node* n) :const_iterator(n) {};
public:
iterator() : const_iterator() {};
//prefix
iterator& operator++() {
this->current_ = this->current_->next_;
return *this;
}
//post-fix
iterator operator++(int) {
iterator old = *this;
this->current_ = this->current_->next_;
return old;
}
iterator& operator--() {
this->current_ = this->current_->prev_;
return *this;
}
iterator operator--(int) {
iterator old = *this;
this->current_ = this->current_->prev_;
return old;
}
T& operator*() {
return this->current_->data_;
}
const T& operator*()const {
return this->current_->data;
}
};
SortedList(); //done
~SortedList();
SortedList(const SortedList& rhs);
SortedList& operator=(const SortedList& rhs);
SortedList(SortedList&& rhs);
SortedList& operator=(SortedList&& rhs);
iterator begin() {
return iterator(front_->next_);
}
iterator end() {
return iterator(back_);
}
const_iterator cbegin() const {
return const_iterator(front_->next_);
}
const_iterator cend() const {
return const_iterator(back_);
}
iterator insert(const T& data);
iterator search(const T& data);
const_iterator search(const T& data) const;
iterator erase(iterator it);
void merge(SortedList& other);
bool empty() const;
int size() const;
};
first merge function attempt
template <typename T>
void SortedList<T>::merge(SortedList& other) {
iterator it = this->begin();
iterator oit = other.begin();
while (oit != other.end()) {
std::cout << *oit << " " << *it << std::endl;
if (*oit < *it) {
oit.current_->prev_->next_ = oit.current_->next_;
oit.current_->next_->prev_ = oit.current_->prev_;
oit.current_->next_ = it.current_;
oit.current_->prev_ = it.current_->prev_;
it.current_->next_ = oit.current_;
}
else {
oit.current_->prev_->next_ = oit.current_->next_;
oit.current_->next_->prev_ = oit.current_->prev_;
oit.current_->next_ = it.current_->next_;
oit.current_->prev_ = it.current_;
it.current_->prev_ = oit.current_;
}
oit++;
it++;
}
}
main tester
int main() {
int num[] = { 3,5,1,2,6,8,9,11 };
int num2[] = { 1,5,4,6,12,7,8,9 };
SortedList<int> l;
SortedList<int> l2;
for (int i = 0; i < 8; i++)
{
l.insert(num[i]);
l2.insert(num2[i]);
}
SortedList<int>::iterator result;
SortedList<int>::iterator result2 = l2.begin();
result = l.begin();
while (result != l.end()) {
std::cout << *result << " " << *result2 << std::endl;
++result;
++result2;
}
l.merge(l2);
output
1 1
2 4
3 5
5 6
6 7
8 8
9 9
11 12
1 1
2 2
3 3
5 5
6 6
8 8
9 9
11 11
0 0
I dont understand why my second output is showing same the same values for *it and *oit I am pretty certain the error is in how I assign the the oit.current_->next & prev but i am unsure.
any insight is appriciated.
You seem to want to merge two sorted doubly linked lists together. There are several problems with your approach, and so I'll show you my code:
#include <iostream>
using namespace std;
struct node {
node* next;
node* prev;
int val;
node(int i_val)
: next(nullptr),
prev(nullptr),
val(i_val)
{}
};
void connect(node* a, node* b) {
if (a != nullptr) {
if (a->next != nullptr) {
a->next->prev = nullptr;
}
a->next = b;
}
if (b != nullptr) {
if (b->prev != nullptr) {
b->prev->next = nullptr;
}
b->prev = a;
}
}
struct DlList {
node* first_node;
node* last_node;
DlList()
: first_node(nullptr),
last_node(nullptr)
{}
~DlList() {
for (node* n = first_node; n != nullptr; n = n->next) {
delete n->prev;
}
delete last_node;
}
void push(int new_val) {
node* new_node = new node(new_val);
connect(last_node, new_node);
last_node = new_node;
if (first_node == nullptr) {
first_node = new_node;
}
}
void merge_sorted(DlList& other) {
node* this_node = first_node;
node* other_node = other.first_node;
node* n = nullptr; // goes through each node of the new list in order
while (this_node != nullptr || other_node != nullptr) {
node* next_n;
if (other_node == nullptr ||
(this_node != nullptr && this_node->val <= other_node->val)) {
// entered if other_node is nullptr or this_node comes before other_node
next_n = this_node;
this_node = this_node->next;
}
else {
// entered if this_node is nullptr or other_node comes before this_node
next_n = other_node;
other_node = other_node->next;
}
connect(n, next_n);
if (n == nullptr) { // first time through loop
first_node = next_n;
}
n = next_n;
}
last_node = n;
// *this takes ownership of all of other's nodes
other.first_node = nullptr;
other.last_node = nullptr;
}
};
int main() {
std::cout << "running test" << std::endl;
int num[] = { 1,2,3,5,6,8,9,11 };
int num2[] = { 1,4,5,6,7,8,9,12 };
DlList l;
DlList l2;
for (int i = 0; i < 8; i++)
{
l.push(num[i]);
l2.push(num2[i]);
}
l.merge_sorted(l2);
for (node* n = l.first_node; n != nullptr; n = n->next) {
std::cout << n->val << " ";
}
std::cout << std::endl;
}
You may add iterators and other higher-level abstractions later, but for now I think they complicate and obscure the logic. I also did not see a need for a "past-the-end-of-the-list" node in your case, as nullptr would suffice. Though of course these could rather easily be added in if you wanted them to, just for demonstration purposes they are omitted.
Notice how I made a dedicated connect function that does all the pointer assignments as they should be done for two nodes. It handles a bunch of combinations of nullptr cases, too, so you don't need to worry as much about checking for null pointers outside of the function. (Note how the first time through the merge loop, a null n pointer is connected to next_n). Now you hardly need to worry about pointer assignments, and it's clearer when you just say, "connect these two nodes."
My merger function goes through each node in the newly created list. It picks the next node from the two available nodes, from *this and other. It then connects the current node to the next node, and advances the current node to the next node. It has special handling when one or the other of the lists runs out (this_node or other_node becomes nullptr), which indeed happens in the given test case. It takes care to assign first_node and last_node in the correct places, and to clear other after the merge, so as to prevent double ownership issues.
I am implementing a generic doubly linked list container with a nested custom iterator in the container class. My problem is that when compiling I get:
Error C2678 binary '*': no operator found which takes a left-hand operand of type 'const List::ListIter' (or there is no acceptable conversion)
This error points to when trying to dereference a iterator on line 246 for example. The operator* for the iterator is implemented in the iterator class but the compiler does not seem to be able to find the overload.
Everything else seems to compile just fine when buildning
using std::cout;
template<class T> class List {
template<class T>
class Node;
public:
template <class T>
class Link {
public:
friend class List<T>;
Link* _next, *_prev;
Link(Link *n, Link *p) : _next(n), _prev(p) {}
Node<T>* Next() { return static_cast<Node<T>*>(_next); }
Node<T>* Prev() { return static_cast<Node<T>*>(_next); }
};
template<class T>
class Node :public Link<T> {
public:
friend class List<T>;
Node(const T& data = NULL, Link<T> *n = NULL, Link<T> *p = NULL) :_data(data), Link<T>(n, p) {}
T _data;
};
public:
template<class T> class ListIter {
public:
typedef ptrdiff_t difference_type;
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef std::bidirectional_iterator_tag iterator_category;//behvöber fixas
ListIter(Node<T>* p) {
_ptr = p;
}
ListIter() {
_ptr = nullptr;
}
ListIter(const ListIter& other) {
_ptr = other._ptr;
}
ListIter& operator=(const ListIter& other) {
if (_ptr == other._ptr) {
return *this;
}
else {
_ptr = other._ptr;
return *this;
}
}
T & operator*() {
return _ptr->_data;
}
T* operator->() {
return &_ptr->_data;
}
ListIter& operator++() {
_ptr = static_cast<Node<T>*>(_ptr->_next);
return *this;
}
ListIter& operator--() {
_ptr = static_cast<Node<T>*>(_ptr->Prev());
return *this;
}
ListIter operator++(int) {
ListIter temp(*this);
_ptr = static_cast<Node<T>*>(_ptr->_next);
return temp;
}
ListIter operator--(int) {
ListIter temp(*this);
_ptr = static_cast<Node<T>*>(_ptr->Prev());
return temp;
}
private:
Node<T>* _ptr;
};
using iterator = ListIter<T>;
~List() {
int counter = 0;
Link<T> *curr = _head;
while (curr != nullptr)
{
Link<T> *remove = curr;
curr = curr->_next;
delete remove;
counter++;
}
cout << "removed" << counter << std::endl;
}
List() :_head(nullptr) {};
List(const List& other) {
if (other._head == nullptr) {
_head = nullptr;
}
_head = new Node<T>(other._head->_data);
Node<T> *tempOther = _head->Next();
while (tempOther != other._head) {
push_back(tempOther->_data);
tempOther = tempOther->Next();
}
}
List(List&& other) {
_head = other._head;
other._head = nullptr;
}
List(const char* other) {
int i = 0;
while (other[i] != '\0') {
push_front(other[i]);
i++;
}
}
List& operator=(const List* other) {
if (*this == *other) {
return *this;
}
else {
if (other->_head != NULL) {
Link<T> *curr = _head;
while (curr != nullptr)
{
Link<T> *remove = curr;
curr = curr->_next;
delete remove;
}
_head = new Node<T>(other->_head->_data);
if (other->_head->Prev() != NULL) {
Node<T> *prev = other->_head->Prev();
while (prev != NULL) {
push_back(prev->_data);
prev = prev->Prev();
}
}
other->~List();
}
else {
throw std::exception("list empty");
}
}
return *this;
}
List& operator=(List&& other) {
while (_head->_next != _head)
{
Link<T> *remove = _head->_next;
_head->_next = remove->_next;
delete remove;
}
delete _head;
_head = other._head;
return *this;
}
T& front() {
if (_head->_next != NULL) {
return _head->Next()->_data;
}
else {
throw std::exception("list is empty");
}
}
T& back() {
if (!empty()) {
Node<T> *last = _head->Prev();
return last->_data;
}
else {
throw std::exception("list is empty");
}
}
iterator begin() const {
return ListIter<T>(_head);
}
iterator end() const {
return ListIter<T>(_head->Prev());
}
bool empty() const noexcept {
if (_head == NULL) {
return true;
}
else {
return false;
}
}
size_t size() const noexcept {
size_t counter = 0;
if (empty()) {
return 0;
}
else if (_head->Next() == _head->Prev()) {
return 1;
}
else {
Link<T> *last = _head->_next;
while (last != _head) {
last = last->_next;
counter++;
}
}
return counter;
}
iterator insert(const iterator& pos, const T& value) {
if (empty()) {
throw std::exception("list empty");
}
else {
Node<T> prev = *pos->Prev();
Node<T> newNode = new Node<T>(value, *pos, prev);
prev->_next = newNode;
*pos->_prev = newNode;
}
}
ListIter erase(const iterator& pos) {
Link<T> next = (*pos)->_next;
Link<T> prev = (*pos)->_prev;
next._prev = prev;
prev._next = next;
delete *pos;
}
void push_front(const T& value) {
if (empty()) {
_head = new Node<T>(value);
}
else if (size() == 1) {
Node<T> *newHead = new Node<T>(value, _head, _head);
_head->_next = newHead;
_head->_prev = newHead;
_head = newHead;
}
else {
Node<T> *newhead = new Node<T>(value, _head, _head->Prev());
_head = newhead;
}
}
void push_back(const T& value) {
if (empty()) {
_head = new Node<T>(value);
}
else if (size() == 1) {
Node<T> *last = new Node<T>(value, _head, _head);
_head->_next = last;
_head->_prev = last;
}
else {
Link<T> *last = _head->Prev();
Node<T> *newLast = new Node<T>(value, _head, last);
last->_next = newLast;
}
}
void pop_back() {
if (empty()) {
throw std::exception("list is empty");
}
else {
Link<T> *last = _head->Prev();
Link<T> *beforeLast = last->Prev();
beforeLast->_next = _head;
delete last;
}
}
void pop_front() {
if (empty()) {
throw std::exception("list is empty");
}
else {
Link<T> *newFront = _head->Next();
Link<T> *temp = _head->Prev();
temp->_next = newFront;
delete _head;
_head = static_cast<Node<T>*>(newFront);
}
}
friend bool operator==(const List& lhs, const List& rhs) {
bool result = true;
if (lhs.size() == rhs.size()) {
if (!lhs.empty()) {
Node<T> *lhsNode = lhs._head;
Node<T> *rhsNode = rhs._head;
while (lhsNode->_next != NULL && rhsNode->Next() != NULL) {
if (lhsNode->_data != rhsNode->_data) {
result = false;
}
}
}
else {
throw std::exception("list empty");
}
}
else {
result = false;
}
return result;
}
friend std::ostream& operator<<(std::ostream& cout, const List& other);
void Check() const {
/*assert(Invariant());*/
}
template< class T>
void swap(List<T>& lhs, List<T>& rhs) {
Node<T> temp = rhs._head;
rhs._head = lhs._head;
lhs._head = temp;
}
private:
Node<T> *_head;
};
I just need to access the value that the dereferenced iterator is supposed to give.
In insert and erase you have const iterator& pos. Since you pass by reference to a constant the iterator is const. Your operator* is not marked as const so you cannot use it on a constant object. You either need to provide a const overload or drop the const from the function parameter.
I am currently creating a square list and one of the requirements is to overload the the pre- and postfix ++ operator.
I've tried to overload the ++operator in my .hpp file to increment an iterator. But when calling the ++operator it does not call the overloaded code and just uses the default.
iterator& operator ++ () { // pre-increment
std::list<int>::iterator i = list_Values.begin();
advance(i,1);
return &*i;
}
iterator operator ++ (int) { // post-increment
std::list<int>::iterator i = list_Values.begin();
advance(i,1);
return &*i;
}
I try to invoke the ++operator this way:
int* test = sqrList.begin();
++test;
test++;
int* test = sqrList.begin();
++test;
test++;
You're incrementing a int*, and since there's no int* class to extend, you cannot possibly have actually created operators for this type.
Create the operators for the correct type, instead. You probably mean to make your own Iterator type.
You have to declare the operator inside the class.
class Iterator
{
public:
Iterator& operator ++ ()
{
return *this;
}
Iterator operator ++ (int)
{
return *this;
}
};
Also, why are you using return &*i;?
Note that these operators need to be declared inside the Iterator class, not inside the container.
For novices it is sometimes hard to understand an issue out of context. So I decided to provide a more or less complete example of defining custom iterator and overloading its operators.
Notice, when overloading post increment operator, we return iterator by value, not by reference.
Of course, this is not a real world example, where we would not use raw pointers.
#include<iostream>
#include<assert.h>
struct node
{
int data;
node* next;
node() :data(0), next(nullptr) {}
node(int d) :data(d), next(nullptr) {}
};
//forward declaration
class MyList;
class MyListIterator
{
private:
node* _ptr;
friend class MyList;
explicit MyListIterator(node* n) : _ptr(n) {}
public:
int& operator*() const {
return _ptr->data;
}
//overload pre increment operator
MyListIterator& operator++() {
_ptr = _ptr->next;
return *this;
}
// overload post increment operator
MyListIterator operator++(int) {
MyListIterator ret = *this;
++* (this);
return ret;
}
bool operator==(const MyListIterator& iter) const {
return this->_ptr == iter._ptr;
}
bool operator!=(const MyListIterator& iter) const {
return this->_ptr != iter._ptr;
}
};
class MyList
{
private:
node* _head;
node* _tail;
size_t _size;
public:
MyList() : _head(nullptr), _tail(nullptr), _size(0) {};
void push_back(int d) {
node* newTail = new node(d);
if (_tail != nullptr) {
_tail->next = newTail;
}
else {
_head = newTail;
}
_tail = newTail;
_size++;
}
int& operator[](size_t i) {
if (i >= _size)
throw std::out_of_range("MyList");
node * p = _head;
for (size_t j = 0; j < i; j++) {
p = p->next;
}
return p->data;
}
void remove(int d) {
if (_head == nullptr)
return;
node * p = _head;
node * prev = p;
while ((p != nullptr) && (p->data != d)) {
prev = p;
p = p->next;
}
if (p != nullptr) {
if (p == _head)
_head = p->next;
else
prev->next = p->next;
delete p;
_size--;
}
}
size_t size() const {
return _size;
}
~MyList() {
node* next = nullptr;
for (node* p = _head; p != nullptr; p = next) {
next = p->next;
delete p;
}
}
using iterator = MyListIterator;
iterator begin() { return iterator(_head); }
iterator end() { return iterator(_tail->next); }
};
int main()
{
MyList mylist;
mylist.push_back(1);
mylist.push_back(2);
mylist.push_back(3);
int count = 1;
//pre increment
for (MyListIterator it = mylist.begin(); it != mylist.end(); ++it)
assert(*it == count++);
count = 1;
//post increment
for (MyListIterator it = mylist.begin(); it != mylist.end(); it++)
assert(*it == count++);
for (size_t i = 0; i < 3; i++)
assert(mylist[i] == i + 1);
mylist.remove(2);
assert(mylist[0] == 1);
assert(mylist[1] == 3);
assert(mylist.size() ==2);
mylist.remove(1);
mylist.remove(3);
assert(mylist.size() == 0);
}