Iterator pointer does not reference the first element - c++

I am trying to implement a LinkedList which can be iterated through in c++.
I have therefore made an Iterator class, such that dereferencing an Iterator would return the first element. However, this has not been working. When I then instantiate a new int LinkedList and attempt to access the first element by dereferencing the result of begin(), I do not retrieve the first element of the list, but a 10 digit number such as '1453755360'
My node class is just composed of two right/left node pointers and a data variable
linkedlist class
template <typename T>
class LinkedList{
public:
LinkedList(){
count =(0);
head =(nullptr);
tail =(nullptr);
}
void push_head(T input){
Node<T> newNode = Node<T>(input);
newNode.left = nullptr;
newNode.right = head;
head = &newNode;
count++;
}
T front(){
T& data = (head->data);
return data;
}
void push_tail(T input){
Node<T> newNode = Node<T>(input);
newNode.right = tail;
newNode.left = nullptr;
tail = &newNode;
count++;
}
T back(){
T& data = (tail->data);
return data;
}
Iterator<T> begin(){
Iterator<T> test = Iterator<T>(head);
return test;
}
private:
int count;
Node<T> *head;
Node<T> *tail;
};
Here is where I am testing the code
LinkedList<int> ll;
ll.push_tail(7);
ll.push_tail(9);
if (*(ll.begin()) == 9) {
cout << "pass" << endl;
} else {
cout << "returned : " << *(ll.begin()) << endl;
}

The push_back() implementation requires that if head is null it has to be set, the same for the push_front with respect to the tail.

You are allocating your node objects on the stack, so they are destroyed automatically when they go out of scope. You are storing pointers to those objects, which leaves the pointers dangling when the objects are destroyed. You need to allocate the nodes on the heap using new instead.
Also, push_front() is not updating tail when the list is empty, and is not updating an existing head to point at the new node when the list is not empty. Similar with push_back().
Try something more like this:
template <typename T>
struct Node
{
T data;
Node *left;
Node *right;
Node(const T &d = T(), Node *l = nullptr, Node *r = nullptr)
: data(d), left(l), right(r) {}
};
template <typename T>
class NodeIterator {
public:
typedef std::ptrdiff_t difference_type;
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef std::bidirectional_iterator_tag iterator_category;
NodeIterator(Node<T> *input = nullptr) : cur(input) {}
NodeIterator(const NodeIterator &) = default;
NodeIterator(NodeIterator &&) = default;
~NodeIterator() = default;
NodeIterator& operator=(const NodeIterator &) = default;
NodeIterator& operator=(NodeIterator &&) = default;
reference operator*() {
return cur->data;
}
NodeIterator& operator++ () {
if (cur) cur = cur->right;
return *this;
}
NodeIterator operator++ (int) {
NodeIterator tmp(*this);
if (cur) cur = cur->right;
return tmp;
}
NodeIterator& operator-- () {
if (cur) cur = cur->left;
return *this;
}
NodeIterator operator-- (int) {
NodeIterator tmp(*this);
if (cur) cur = cur->left;
return tmp;
}
bool operator==(const NodeIterator &rhs) const {
return (rhs.cur == cur);
}
bool operator!=(const NodeIterator &rhs) const {
return (rhs.cur != cur);
}
private:
Node<T> *cur;
};
template <typename T>
class LinkedList {
public:
typedef NodeIterator<T> iterator;
LinkedList() : count(0), head(nullptr), tail(nullptr) {}
~LinkedList() {
while (head) {
Node<T> *tmp = head;
head = head->right;
delete tmp;
}
}
void push_front(const T &input) {
Node<T> *newNode = new Node<T>(input, nullptr, head);
if (head) head->left = newNode;
head = newNode;
if (!tail) tail = newNode;
++count;
}
T& front() {
return head->data;
}
void push_back(const T &input) {
Node<T> *newNode = new Node<T>(input, tail, nullptr);
if (!head) head = newNode;
if (tail) tail->right = newNode;
tail = newNode;
++count;
}
T& back() {
return tail->data;
}
iterator begin() {
return iterator(head);
}
iterator end() {
return iterator();
}
private:
int count;
Node<T> *head;
Node<T> *tail;
};
Then you can do this:
LinkedList<int> ll;
ll.push_back(7);
ll.push_back(9);
auto iter = ll.begin();
if (*iter == 7) {
cout << "pass" << endl;
} else {
cout << "returned : " << *iter << endl;
}
You can even do things like this now:
for (LinkedList<int>::iterator iter = ll.begin(), end = ll.end(); iter != end; ++iter) {
cout << *iter << endl;
}
for (int i : ll) {
cout << i << endl;
}

Related

error: new initializer expression list treated as compound expression [-fpermissive]

I am trying to write a playlist method for songs in c++, however, I keep running into frequent errors.
template <typename T>
struct cir_list_node
{
T* data;
cir_list_node *next, *prev;
//destructor
~cir_list_node() {
delete data;
}
};
template <typename T>
struct cir_list
{
public:
using node = cir_list_node<T>; // renaiming for local use
using node_ptr = node*;
private:
node_ptr head;
size_t n;
public:
// basic constructor and size function
cir_list(): n(0) // initiator list
{
head = new node(NULL,NULL,NULL); //dummy node
head->next = head; // circular list wraps around
head->prev = head;
}
cir_list(const cir_list& other): cir_list()
{
// insert in reverse order (okay because list is circular)
for(const auto& i : other)
insert(i);
}
cir_list(const std::initializer_list<T>& il): head(NULL), n(0)
{
//insert in reverse order
for(const auto& i : il)
insert(i);
}
~cir_list()
{
while(size())
{
erase(head->data);
}
}
size_t size() const
{
return n;
}
void insert(const T& value)
{
node_ptr newNode = new node(new T(value),NULL,NULL);
n++;
auto dummy = head->prev;
dummy->next = newNode;
newNode->prev = dummy;
if(head == dummy) {
dummy->prev = newNode;
newNode->next = dummy;
head = newNode;
return;
}
newNode->next = head;
head->prev = newNode;
head = newNode;
return;
}
void erase(const T& value)
{
auto cur = head, dummy = head->prev;
while (cur != dummy){
if(*(cur->data) == value){
cur->prev->next = cur->next;
cur->next->prev = cur->prev;
if(cur == head)
head = head->next;
delete cur;
n--;
return;
}
cur = cur->next;
}
}
struct cir_list_it
{
private:
node_ptr ptr;
public:
cir_list_it(node_ptr p) : ptr(p)
{}
T& operator*()
{
return *(ptr->data);
}
node_ptr get()
{
return ptr;
}
cir_list_it& operator++() // prefix
{
ptr = ptr->next;
return *this;
}
cir_list_it operator++(int) // postfix
{
cir_list_it it = *this;
++(*this);
}
cir_list_it& operator--() //prefix
{
ptr = ptr->prev;
return *this;
}
cir_list_it operator--(int) // postfix
{
cir_list_it it = *this;
--(*this);
return it;
}
friend bool operator == (const cir_list_it& it1, const cir_list_it& it2)
{
return it1.ptr == it2.ptr;
}
friend bool operator != (const cir_list_it& it1, const cir_list_it& it2)
{
return it1.ptr != it2.ptr;
}
};
cir_list_it begin()
{
return cir_list_it {head};
}
cir_list_it end()
{
return cir_list_it {head->prev};
}
cir_list_it end() const
{
return cir_list_it{head->prev};
}
};
struct playlist
{
cir_list<int> list;
void insert(int song)
{
list.insert(song);
}
void erase(int song)
{
list.erase(song);
}
void loopOnce()
{
for(auto& song : list){
std::cout << song << " ";
}
std::cout << std::endl;
}
};
int main()
{
playlist pl;
pl.insert(1);
pl.insert(2);
std::cout << "Playlist: ";
pl.loopOnce();
playlist pl2 = pl;
pl2.erase(2);
pl2.insert(3);
std::cout << "Second playlist";
pl2.loopOnce();
}
Errors
1 and 2:
3 and 4:
It seems there is a typo
struct cir_list_node
{
T* data;
cir_list_node *next, *prev;
//destructor
~cir_list_node() {
delete data;
}
};
You forgot to prefix this declaration with
template <typename T>
This template structure declaration declares an aggregate.
You can not initialized it with an expression inside parentheses like
head = new node(NULL,NULL,NULL);
Instead you need to write using braces
head = new node { NULL, NULL, NULL };
or as you are writing a C++ program then
head = new node { nullptr, nullptr, nullptr };
It seems there is a typo
struct cir_list_node
{
T* data;
cir_list_node *next, *prev;
//destructor
~cir_list_node() {
delete data;
}
};

"blocks are indirectly lost in loss record.." valgrind error - cpp

I am trying to figure out what is wrong with my cpp code, and i need your help!
valgrind Output:
the error occurs here (in the test file):
list = list.apply(getLen);
the function getLen:
string getLen(string str)
{
return std::to_string(str.length());
}
the generic function apply:
template<class T>
template<class Operation>
SortedList<T> SortedList<T>::apply(Operation operation) const{//TODO: 6 blocks are indirectly lost.
Node<T>* current_node_to_check = head;
SortedList<T> new_sorted_list;
for (int i = 0; i < list_length; ++i) {
T result = operation(current_node_to_check->info);
new_sorted_list.insert(result);
current_node_to_check = current_node_to_check->next;
}
return new_sorted_list;
}
the Node struct implementation:
template<class T>
struct Node{
T info;
Node<T>* next;
explicit Node(const T& new_info) : info(new_info), next(nullptr){}
};
the test file:
#include <iostream>
#include "SortedList.h"
#include "examDetails.h"
using std::cout;
using std::endl;
using std::string;
using namespace mtm;
#define TEST(num) cout << endl << "TEST " << (num) << endl;
string getLen(string str)
{
return std::to_string(str.length());
}
bool isTrollLink(const ExamDetails& exam) {
return (exam.getLink().find("tinyurl") != string::npos);
}
template<class T>
void printList(SortedList<T> list) {
for (auto it = list.begin(); !(it == list.end()); ++it) {
cout << *it << endl;
}
cout << endl;
}
int main()
{
.
.
.
TEST("1.5")
SortedList<string> lst1 = SortedList<string>();
lst1.insert("Charlie");
lst1.insert("Bob");
lst1.insert("Alice");
lst1.insert("Donald");
printList(lst1);
TEST("1.6")
SortedList<ExamDetails> lst2;
lst2.insert(exam1);
lst2.insert(exam2);
printList(lst2);
TEST("1.7")
SortedList<string> lst3 = lst1;
printList(lst3);
TEST("1.8")//TODO: 6 blocks are indirectly lost.
lst3 = lst3.apply(getLen);
printList(lst3);
TEST("1.9")
lst3.remove(lst3.begin());
printList(lst3);
TEST("1.10")
SortedList<ExamDetails> lst4 = lst2.filter(isTrollLink);
printList(lst2);
cout << "----------" << endl;
printList(lst4);
return 0;
}
the generic class file:
#ifndef NEW_SORTED_LIST_SORTEDLIST_H
#define NEW_SORTED_LIST_SORTEDLIST_H
#include <stdexcept>
using std::cout;
using std::endl;
namespace mtm {
template<class T>
struct Node{
T info;
Node<T>* next;
explicit Node(const T& new_info) : info(new_info), next(nullptr){}
};
template<class T>
class SortedList {
private:
Node<T>* head;
int list_length;
static const int empty = 0;
void deleteNodes();
public:
class const_iterator;
SortedList();
~SortedList();
SortedList(const SortedList &to_copy);
SortedList& operator=(const SortedList &to_assign);
void insert(const T &to_insert);
void remove(const const_iterator& iterator);
int length() const;
template<class Condition>
SortedList filter(Condition condition) const;
template<class Operation>
SortedList apply(Operation operation) const;
const_iterator begin() const;
const_iterator end() const;
};
template<class T>
class SortedList<T>::const_iterator{
const SortedList<T>* sortedListPointer;
int index;
const_iterator(const SortedList<T>* sortedListPointer, int index) : sortedListPointer(sortedListPointer),
index(index){}
friend class SortedList<T>;
static const int first = 1;
public:
const_iterator(const const_iterator&) = default;
const_iterator& operator=(const const_iterator&) = default;
const T& operator*(){
if (index > sortedListPointer->length()+1 || index<0){
throw std::out_of_range("error: iterator out of range");//TODO
}
Node<T>* current = sortedListPointer->head;
for (int i = 1; i < index; ++i) {
current = current->next;
}
return current->info;
}
const_iterator& operator++() {
++index;
if (index > sortedListPointer->length()+1){
throw std::out_of_range("error: iterator out of range");//TODO
}
return *this;
}
const_iterator operator++(int){
SortedList<T>::const_iterator result = *this;
++*this;
return result;
}
bool operator==(const const_iterator& it) const{
return (index == it.index);
}
};
template<class T>
typename SortedList<T>::const_iterator SortedList<T>::begin() const{
return const_iterator(this, const_iterator::first);
}
template<class T>
typename SortedList<T>::const_iterator SortedList<T>::end() const{
return const_iterator(this, this->length()+1);
}
template<class T>
SortedList<T>::SortedList() : head(nullptr), list_length(empty){}
template<class T>
SortedList<T>::~SortedList() {
deleteNodes();
}
template<class T>
SortedList<T>::SortedList(const SortedList& to_copy){
Node<T>* current_to_copy = to_copy.head;
Node<T>* current = nullptr;
Node<T>* new_head;
if (to_copy.list_length == 0){
return;
}else{
Node<T>* new_node = new Node<T>(current_to_copy->info);
//exception
//new_node->info = current_to_copy->info;
//new_node->next = nullptr;
current = new_node;
current_to_copy = current_to_copy->next;
new_head = current;
}
while (current_to_copy){
Node<T>* new_node = new Node<T>(current_to_copy->info);
//exception
//new_node->info = current_to_copy->info;
//new_node->next = nullptr;
current->next = new_node;
current = current->next;
current_to_copy = current_to_copy->next;
}
list_length = to_copy.list_length;
head = new_head;
}
template<class T>
void SortedList<T>::deleteNodes(){
if (list_length == empty){
head = nullptr;
return;
}
Node<T>* current = head->next;
for (int i = 1; i < list_length; ++i) {
Node<T>* previous = head;
Node<T>* temp = current->next;
delete current;
current = temp;
previous->next = current;
}
delete head;
head = nullptr;
list_length = empty;
}
template<class T>
SortedList<T>& SortedList<T>::operator=(const SortedList &to_assign){
if (this == &to_assign){
return *this;
}
head = nullptr;
Node<T>* current_to_assign = to_assign.head;
Node<T>* current;
Node<T>* new_head;
if (current_to_assign){
Node<T>* first_node = new Node<T>(current_to_assign->info);
current = first_node;
current_to_assign = current_to_assign->next;
new_head = first_node;
}else{
deleteNodes();
head = nullptr;
return *this;
}
while (current_to_assign){
Node<T>* new_node = new Node<T>(current_to_assign->info);
current->next = new_node;
current = current->next;
current_to_assign = current_to_assign->next;
}
head = new_head;
list_length = to_assign.list_length;
return *this;
}
template<class T>
void SortedList<T>::insert(const T &to_insert){
Node<T>* new_node = new Node<T>(to_insert);
Node<T>* current = head;
if (!current){
head = new_node;
list_length++;
return;
}
Node<T>* previous = head;
if (current->info < to_insert){
current = current->next;
}else{
new_node->next = current;
head = new_node;
list_length++;
return;
}
while (current){
if (current->info < to_insert){
current = current->next;
previous = previous->next;
}else{
new_node->next = current;
previous->next = new_node;
list_length++;
return;
}
}
previous->next = new_node;
list_length++;
}
template<class T>
void SortedList<T>::remove(const SortedList<T>::const_iterator& iterator){
if (list_length == 1){
list_length--;
delete head;
}
int index = iterator.index;
if (index == 1){
Node<T>* temp = head->next;
delete head;
head = temp;
list_length--;
return;
}
Node<T>* current = head;
Node<T>* previous = nullptr;
for (int i = 1; i < index-1; ++i) {
if ((i+1) == index-1){
previous = current;
}
current = current->next;
}
previous->next = current->next;
delete current;//TODO destructor(?)
list_length--;
}
template<class T>
int SortedList<T>::length() const{
return list_length;
}
template<class T>
template<class Condition>
SortedList<T> SortedList<T>::filter(Condition condition) const{
Node<T>* current_node_to_check = head;
SortedList<T> new_sorted_list;
for (int i = 0; i < list_length; ++i) {
if (condition(current_node_to_check->info)){
new_sorted_list.insert(current_node_to_check->info);
}
current_node_to_check = current_node_to_check->next;
}
return new_sorted_list;
}
template<class T>
template<class Operation>
SortedList<T> SortedList<T>::apply(Operation operation) const{//TODO: 6 blocks are indirectly lost.
Node<T>* current_node_to_check = head;
SortedList<T> new_sorted_list;
for (int i = 0; i < list_length; ++i) {
T result = operation(current_node_to_check->info);
new_sorted_list.insert(result);
current_node_to_check = current_node_to_check->next;
}
return new_sorted_list;
}
}
#endif //NEW_SORTED_LIST_SORTEDLIST_H
without the test 1.8, the code works great with no errors at all!
What should I do to fix it?
thanks
You allocated a SortedList<string> object in main() which allocated memory and never freed it.
The other blocks belong to the std::string objects inside the list. They are only lost because the list holding them lost track of them.
When doing your analysis, don't worry about the 6 indirectly lost blocks, focus on the one directly lost one.
Almost certainly this is a violation of the "Rule of Five". Your SortedList does an allocation in its constructor and doesn't properly manage that allocation in its move-assignment operator.
Looking at your code in the copy assignment operator, this line is causing the leak
head = nullptr;
It shouldn't be needed, you later call deleteNodes() which will do that anyway, and by erasing head beforehand you prevent deleteNodes() from doing its work.

cannot convert 'this' pointer from 'const List<T>' to 'List<T> &'

I implement a doubly linked list. Then I used that linked list as container for stack. but I get Error C2662 'const T &List::last_elem(void)': cannot convert 'this' pointer from 'const List' to 'List &'. I tried to return as a value but it didnt work. I dont understand whether compiler points wrong point.
template<typename T>
struct Node
{
T data;
Node* next;
Node* prev;
Node() :data{ 0 } { next = nullptr; prev = nullptr; }
};
template<typename T>
class List
{
private:
Node<T>* headNode;
Node<T>* tailNode;
public:
List()
{
headNode = new Node<T>;
tailNode = new Node<T>;
headNode->next = tailNode;
tailNode->prev = headNode;
}
~List()
{
Node<T>* current = headNode;
while (current)
{
Node<T>* tempNode = current;
current = current->next;
delete tempNode; cout << "\nDeleting List!!!";
}
}
bool empty()
{
return (headNode->next == tailNode);
}
const T &last_elem()
{
return tailNode->prev->data;
}
const T &first_elem()
{
return headNode->next->data;
}
void remove_first()
{
Node<T>* tempNode = headNode;
headNode = tempNode->next;
delete tempNode; cout << "\nDeleting Node!!!";
headNode->prev = nullptr;
}
void remove_last()
{
Node<T>* tempNode = tailNode;
tailNode = tempNode->prev;
delete tempNode; cout << "\nDeleting Node!!!";
tailNode->next = nullptr;
}
void add_front(T d)
{
headNode->data = d;
Node<T>* tempNode = new Node<T>;
tempNode->next = headNode;
headNode->prev = tempNode;
headNode = tempNode;
}
void add_end(T d)
{
tailNode->data = d;
Node<T>* tempNode = new Node<T>;
tempNode->prev = tailNode;
tailNode->next = tempNode;
tailNode = tempNode;
}
void print_list()
{
Node<T>* current = headNode->next;
while (current)
{
cout << current->data << "|-> ";
current = current->next;
}
}
void reverse_print_list()
{
Node<T>* current = tailNode->prev;
while (current)
{
cout << current->data << "|-> ";
current = current->prev;
}
}
};
template<typename T>
class ListStack
{
private:
List<T> stacklist;
int index;
public:
class StackException
{
private:
string errMessage;
public:
StackException(string err) :errMessage(err) {}
string getErrMessage() { return errMessage; }
};
ListStack():index { -1 }{}
int size() const // number of items in the stack
{
return index + 1;
}
bool empty() const // is the stack empty?
{
return (index == -1);
}
const T& top() const throw(StackException) // the top element
{
if (empty())throw StackException(string("Stack is empty!!!"));
return stacklist.last_elem();
}
void push(const T& e) // push element onto stack
{
stacklist.add_end(e);
++index;
}
void pop() throw(StackException) // pop the stack
{
if (empty())throw StackException(string("Stack is empty!!!"));
stacklist.remove_last();
--index;
}
};
int main()
{
try
{
ListStack<int> myls;
myls.push(5);
myls.push(8);
myls.push(9);
myls.push(12);
myls.push(17);
cout << myls.top() << endl;
myls.pop();
myls.pop();
myls.pop();
myls.pop();
myls.pop();
myls.pop();
myls.pop();
myls.pop();
myls.pop();
}
catch (ListStack<int>::StackException se)
{
cout << se.getErrMessage();
}
return 0;
}
Make last_elem() a const qualified with:
const T &last_elem() const
// ^^^^^
The Problem is:
const T& top() const throw(StackException)
remove the second const to
const T& top() throw(StackException)
And you should be fine.
(this is because the called last_elem() method is not const since constant methods can obviously not call non-constant methods, so making that one const would work too).

for_each for Node manually c++

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;

Polymorphic function for Print & reverse Print in C++ within a single class

I have written two methods for printing my linked list data. One is print which works normally. And one is for printing the list in reverse order. I want to make my print method to accept a Node* ptr and do the rest of the work as usual. I'm not getting the idea how to make it work because of the operator overloading. My methods are as follows. Please ignore the void* casting and return in the reversePrint method.
template <class T>
std::ostream& operator <<(std::ostream& str, LinkedList<T> const& data){
data.print(str);
return str;
}
template <class T>
void LinkedList<T>::print(std::ostream& str) const{
Node *curr = head;
while(curr != nullptr){
str << curr->data << "\n";
curr = curr->next;
}
}
template <class T>
void* LinkedList<T>::printReverse(std::ostream& str) {
Node* curr = head;
//Node* last = (Node*)getLastNode(head);
Node* rHead = nullptr;
while(curr != nullptr){
Node* temp = curr->next;
curr->next = rHead;
rHead = curr;
curr = temp;
}
while(rHead != nullptr){
std::cout << rHead->data << "\n";
rHead = rHead->next;
}
return (void*)rHead;
}
class definition.
template <class T>
class LinkedList {
private:
struct Node {
T data;
Node *next;
Node(T data, Node *next) :
data(data), next(next) {
}
};
Node* head;
public:
LinkedList() :
head(nullptr) {
}
~LinkedList() {
Node *temp;
for (; head; head = temp) {
temp = head->next;
std::cout << "Destructor called for " << temp->data;
delete head;
}
}
void append(T item);
void insert_at_head(T value);
void* getLastNode(Node* n);
void print(std::ostream& str = std::cout) const;
void* printReverse(std::ostream& str = std::cout);
};
printReverse probably should not modify the list.
I would write it recursively. Idea:
template <class T> void LinkedList<T>::printReverse(std::ostream &str, Node const* head) {
if (!head) return;
printReverse(str, head->next);
std::cout << head->data << "\n";
}
With e.g. declarations in-class:
void printReverse(std::ostream &str) const { printReverse(str, head); }
private:
void static printReverse(std::ostream &str, Node const* head);
Off-topic:
consider const-correctness
consider Command/Query Separation (separate node iteration from printing)
Live On Coliru
#include <iostream>
template <class T> class LinkedList {
private:
struct Node {
T data;
Node *next;
//Node(T data, Node *next) : data(data), next(next) {}
};
Node *head;
public:
LinkedList() : head(nullptr) {}
~LinkedList() {
while (head) {
Node* temp = head->next;
std::cout << "Destructor called for " << head->data;
delete head;
head = temp;
}
}
void append(T item) {
Node* t = getLastNode(head);
(t?t->next : head) = new Node { item, nullptr };
}
void insert_at_head(T value);
Node const*getLastNode(Node const*n) const {
for (Node const* it = n; it; it = it->next)
if (!it->next) return it;
return nullptr;
}
Node*getLastNode(Node*n) {
for (Node* it = n; it; it = it->next)
if (!it->next) return it;
return nullptr;
}
void print(std::ostream &str = std::cout) const;
void printReverse(std::ostream &str) const { printReverse(str, head); }
private:
void static printReverse(std::ostream &str, Node const* head);
};
template <class T> std::ostream &operator<<(std::ostream &str, LinkedList<T> const &data) {
data.print(str);
return str;
}
template <class T> void LinkedList<T>::print(std::ostream &str) const {
for (Node const* curr=head; curr; curr = curr->next) {
str << curr->data << "\n";
}
}
template <class T> void LinkedList<T>::printReverse(std::ostream &str, Node const* head) {
if (!head) return;
printReverse(str, head->next);
std::cout << head->data << "\n";
}
int main() {
LinkedList<int> ll;
ll.append(1);
ll.append(2);
ll.append(3);
ll.append(4);
ll.print(std::cout);
ll.printReverse(std::cout);
}
Prints
1
2
3
4
4
3
2
1