Overloading subscript operator in templated doubly linked list class - c++

I'm trying to implement overloading of the subscript operator in my doubly linked list class, but I am facing problems I've been unable to overcome by myself. I am also pretty new to C++.
This is what I have now.
Outtake from DList class:
T &operator[](int index) {
lookAt = root;
for (size_t i = 0; i < index; i++) {
lookAt = lookAt->getNext();
}
return lookAt->getItem();
}
Node<T>* root;
Node<T>* lookAt;
Node<T>* temp;
Node class:
template <class T>
class Node {
public:
Node() {
this->setNext(nullptr);
this->setPrev(nullptr);
}
Node *getNext() const {
return next;
}
void setNext(Node *next) {
Node::next = next;
}
Node *getPrev() const {
return prev;
}
void setPrev(Node *prev) {
Node::prev = prev;
}
T getItem() const {
return item;
}
void setItem(T item) {
Node::item = item;
}
private:
Node* next;
Node* prev;
T item;
};
The error I keep getting is this:
invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int' return lookAt[index].getItem();
Which leads me to believe there's some kind of problem with the way my item variable is referenced, or/and the return part of the overloading function.
Would appreciate any help/guidance with this.
Cheers

Your getItem() returns a T-type variable, which is a copy of the real data, item. (rvalue)
Your [] operator tries to return an T&, which is an lvalue (reference). However, we can't transform rvalue into lvalue.
One possible solution is to return T& in getItem(), like this:
T& getItem() {
return item; // Now returns a reference (lvalue).
}
Hope it helps.

Related

How does operator++ overloading work to iterate over my LinkedList

This is my LinkedList namespace
namespace LinkedList {
template<class T>
class Node{
public:
Node(const T& data)
:data_(data), next_(nullptr) {}
T data_;
Node<T> *next_;
Node<T> *operator++(){
return next_;
}
};
template<class T>
class LinkedList{
public:
LinkedList()
: head_(nullptr), tail_(nullptr) {}
~LinkedList(){
Node<T> *curr = head_;
Node<T> *next;
while(curr != nullptr){
next = curr->next_;
delete curr;
curr = next;
}
}
void Append(const T& data) {
Node<T> *tmp = new Node<T>(data);
if(head_ == nullptr) {
head_ = tmp;
tail_ = tmp;
} else if(head_ == tail_){
head_->next_ = tmp;
tail_ = tmp;
} else {
tail_->next_ = tmp;
tail_ = tmp;
}
}
void Present(){
for(Node<T> *curr = head_; curr != nullptr; curr=curr->next_){
std::cout << curr->data_ << std::endl;
}
}
Node<T> *begin(){
return head_;
}
Node<T> *end(){
return nullptr;
}
private:
Node<T> *head_;
Node<T> *tail_;
};
}
I was reading into Iterators and wanted to make my LinkedList object compatible with range based for loops. This is what I got from reading about range-based for loops
for(auto x: list){ }
is equivalent to
for(; begin != end; ++begin) { //*begin};
I thought I was being cheeky and could skip a couple steps of operator overloading (!=, *) for my LinkedList to work with range based for loops by programming my iterator into my Node class for no other reason besides that I felt like it. This is only my second day of learning C++ so this might be a stupid question but I am still a little confused on why this doesn't work:
LinkedList::LinkedList<int> list;
list.Append(3);
list.Append(5);
for(auto x: list) { //blah blah blah}
I know that my prefix increment is the core issue, when the for loop does ++__begin, what I am hoping it does is ___begin=_begin->next however it does not. Why is this? How the operator overloading works in my mind is is that whatever member variables I reference in the overloading function, it is referencing from the instance I am operating on. And when I return the ptr, it is setting whatever instance I am operating on to this ptr. I know my understanding of this is wrong because it doesn't work, so please someone explain to me how it actually works lol.
You've written a
template<class T>
Node<T>* Node<T>::operator++();
which would get invoked like
Node<int> n;
Node<int> *p = ++n; // calls the pre-increment operator
Note this is quite different to the canonical form
Node<T>& Node<T>::operator++();
which returns a reference to the incremented object that you can use in an expression.
But there's another problem: operator overloading works
When an operator appears in an expression, and at least one of its operands has a class type or an enumeration type, ...
But Node<T>* is not a class type. It's a pointer, and pointers already have their own built-in operators. You can't overload them. There is no Node<T>*::operator++().
Write an iterator. It's not that bad, and they actually work!
NB. Iterators are designed to generalize pointers, and to have a compatible interface. You can use raw pointers if your container is basically an array - because incrementing a pointer is already the right way to iterate over an array.
It's only when we want to provide array-like or pointer-like access to a non-contiguous structure that we have to do this extra work.

What is the difference between "Node<T*> *first" and "Node<T> *first"?

I'm looking into implementing a linked list, using templates.
As it stands, after looking at some guides, I have managed to built a functioning one, but I am wondering what is the purpose of template pointer? The code seems to be using them arbitrarily. I'll exemplify on my header code below:
template <class T>
class LinkedList{};
template <class T>
class LinkedList<T*>{
private:
Node<T*> *first;
int size;
public:
class Iterator{
public:
Iterator(Node<T*> *newElem){
elem = newElem;
}
virtual ~Iterator(){
}
T getValue(){
return *(elem->getValue());
}
void next(){
elem = elem->getNext();
}
void operator++(int i){
next();
}
void operator++(){
next();
}
T operator*(){
return getValue();
}
bool operator==(const Iterator& rhs){
return (elem == rhs.elem);
}
bool operator!=(const Iterator& rhs){
return (elem != rhs.elem);
}
bool hasNext(){
if (elem == NULL)
return false;
return true;
}
private:
Node<T*> *elem;
};
In this specific context, why do we need to declare the node variable or the linked list with < T *>? In my case, it works just fine using < T >, but I'm most likely missing something. The Node class(not listed above) is implemented using < T > as well, so what is actually happening when you add that pointer there?
Many thanks!
The difference is in the content of your Node.
Let's define the Node class:
template <class T>
struct Node
{
T data;
Node * next;
Node * previous;
};
Let's use int as type T and instantiate:
struct Node
{
int data;
Node * next;
Node * previous;
};
Let's use int and instantiate a T *, as in Node<T*> or Node <int *>:
struct Node
{
int * data;
Node * next;
Node * previous;
};
Notice any difference in the data type of the data member?
In one example, data is an int. In the other example, data is a pointer to an int.

Overloading operators for adding two doubly linked lists

I need help with overloading '+' operator for adding together two doubly linked lists. I cannot compile my program due to getting "no match for operator=..." error. I have overloaded '=' operator already but struggle to print the result of the addition to std output. I have also overloaded the << operator. Been trying to figure out what is wrong for hours with no success. Any hints how to tackle this problem and/or solutions to it are very welcome. It is assignment for my OOP class.
Thanks in advance!
EDIT: The basic idea behind the code is to replicate set. Overloaded operators '+' should work as an union and '*' as intersection. I struggle to get the union properly printed to std output. '+=' seems to work fine. '<<' works good as well, but only when it comes to printing out single list.
EDIT:
Errors produced by compiler (g++, output from code::blocks, I have removed compiler notes):
llist3.cpp|149|error: no match for ‘operator=’ (operand types are ‘LList’ and ‘LList’)|
llist3.cpp|106|note: no known conversion for argument 1 from ‘LList’ to ‘LList&’|
llist3.cpp|151|error: no match for ‘operator=’ (operand types are ‘LList’ and ‘LList’)|
llist3.cpp|106|note: no known conversion for argument 1 from ‘LList’ to ‘LList&’|
llist3.cpp|152|error: no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘LList’)|
#include<iostream>
using namespace std;
class LList {
public:
struct Node {
int elem;
Node* succ;
Node* prev;
Node() : succ(0), prev(0), elem(0) {}
};
LList();
LList(LList& list);
~LList();
Node* next();
Node* begin() { curr = head; }
int getElem() { return curr->elem; }
void addElem(int elem);
LList operator+(LList& set);
LList operator+(int elem);
LList& operator+=(LList& set);
LList& operator+=(int elem);
LList& operator=(LList& list);
friend ostream& operator<<(ostream& os, LList& obj);
private:
Node* curr;
Node* head;
Node* tail;
int size;
void pushFront(Node* n);
void pushInside(Node* n);
void pushBack(Node* n);
};
LList::LList() : head(0), tail(0), size(0), curr(0) {}
LList::LList(LList& list) : size(0), curr(0), head(0), tail(0) {
list.curr = list.head;
while(list.curr) {
addElem(list.getElem());
list.next();
}
}
LList::Node* LList::next() {
if (curr)
return (curr = curr->succ);
else
return 0;
}
void LList::addElem(int elem) {
Node* n = new Node;
n->elem = elem;
if (curr) {
if (curr == head && elem < curr->elem) {
pushFront(n);
}
else if (elem > curr->elem) {
curr = curr->succ;
addElem(elem);
}
else if (elem < curr->elem && elem > (curr->prev)->elem) {
pushInside(n);
}
else if (elem < curr->elem) {
curr = curr->prev;
addElem(elem);
}
} else {
pushBack(n);
}
}
void LList::pushFront(Node* n) {
head = n;
n->succ = curr;
curr->prev = n;
n->prev = 0;
curr = n;
size++;
}
void LList::pushInside(Node* n) {
(curr->prev)->succ = n;
n->succ = curr;
n->prev = curr->prev;
curr->prev = n;
size++;
}
void LList::pushBack(Node* n) {
if (!head) {
head = n;
} else {
tail->succ = n;
n->prev = tail;
}
tail = n;
curr = n;
size++;
}
LList::~LList() {
for (curr = head; curr;) {
Node* temp = curr->succ;
delete curr;
curr = temp;
}
}
LList& LList::operator=(LList& list) {
list.begin();
if (this != &list) {
for (curr = head; curr;) {
Node* temp = curr->succ;
delete curr;
curr = temp;
}
while (list.curr) {
addElem(list.getElem());
list.next();
}
}
return *this;
}
ostream& operator<<(ostream& os, LList& list) {
LList::Node* p = list.head;
os << "{ ";
while(p) {
os << p->elem << (p->succ ? ", " : "");
p = p->succ;
}
os << " }" << endl;
return os;
}
LList LList::operator+(LList& set) {
LList temp = *this;
temp += set;
return temp;
}
LList LList::operator+(int elem) {
*this += elem;
return *this;
}
int main() {
LList setA;
setA.addElem(1234);
setA.addElem(1435);
setA.addElem(1100);
LList setB;
setB.addElem(1234);
setB.addElem(1435);
setB.addElem(5100);
setB = setA + 1234; // 1st error here
LList setD;
setD = setA + setB; //2nd
cout << setA + setB << endl; //3rd
}
There is one glaring error in your code:
Node* begin() { curr = head; }
This code invokes undefined behavior, since you are not returning a value. It should be this:
Node* begin() { curr = head; return curr; }
In addition, you should pass your LList by const reference in functions that do not change the LList parameter:
For example:
LList::LList(LList& list);
LList& operator=(LList& list);
friend ostream& operator<<(ostream& os, LList& obj);
should be:
LList::LList(const LList& list);
LList& operator=(const LList& list);
friend ostream& operator<<(ostream& os, const LList& obj);
Please change these and the other functions to pass const references. If you want to see why you should change this, you will see the issue immediately if you tried to do this:
LList list1;
LList list2;
//...
std::cout << list1 + list2;
The operator << is looking for non-const LList objects, but the addition "inline" returns a temporary LList (which will mean that the return value will be const). The code will not compile due to your overloaded operator << accepting only non-const LList.
So you need to change your parameter in operator << to a const LList&.
You have a built in "current" pointer in your list class. This is a grave design error. You are unable to define your functions correctly because of this error.
It is a design error because with this design you cannot iterate over const lists, and this means, among other bad things, that you cannot do anything useful with temporary lists. So when you calculate setA + setB, you cannot assign it to anything,because to assign you need to iterate, so you need a non-const argument to operator= and to the copy constructor. But you cannot bind a temporary to a non-const reference.
Even if you bypass the public interface in the copy constructor and the copy assignment operator, and copy the list directly without using curr, you will have the same problem with any user function that must use the public interface. That is, setA + setB will not be usable as a function argument. You will need to assign it to some variable first, and then pass that variable to the function.
You also cannot take a list and pass it down to some function in the middle of an iteration, and expect to continue to iterate from the place you've left, because anything that iterates the list changes the curr pointer.
The best solution is to get rid of the curr member and make most of your arguments const LList&. While this is not the onky solution, there are many other drawbacks to having a current pointer built into a list class so I won't talk about them.
In order to iterate the list, you have to supply a separate object that can go back and forth over the list, and a separate variant of it that can go back and forth over a const list. This is called an iterator and you need to read up on this concept if you want to do anything in C++.
In your case iterators can be Node* and const Node*. You only need to provide member functions that return the first node in the list, which you already have. Real libraries do the same. They normally wrap the node pointer in a separate iterator class, for various reasons; but for a simple homework this is not necessary (though you can do it if you want).

C++ pass by reference then set a pointer to the object

I am creating a class LinkedList. I am having difficulty adding another node to my list.
Here is what I have so far:
template<typename T>
class LinkedList
{
private:
T element;
T *next;
public:
LinkedList();
LinkedList(T element);
void add(LinkedList<T> &otherList);
void print();
};
template<typename T>
LinkedList<T>::LinkedList()
{
next = NULL;
}
template<typename T>
LinkedList<T>::LinkedList(T element)
{
this->element = element;
next = NULL;
}
template<typename T>
void LinkedList<T>::add(LinkedList<T> &otherList)
{
next = &otherList;
}
template<typename T>
void LinkedList<T>::print()
{
LinkedList<T> *current = this;
while (current != NULL)
{
std::cout << current->element;
current = current->next;
}
}
int main()
{
LinkedList<std::string> myFirst("First");
LinkedList<std::string> mySecond("Second");
myFirst.add(mySecond);
myFirst.print();
return 0;
}
This works however if I make the change:
void add(const LinkedList<T> &otherList);
template<typename T>
void LinkedList<T>::add(const LinkedList<T> &otherList)
{
next = &otherList; //now an error right here
}
Then I get an error stating:
Assigning to 'LinkedList<std::__1::basic_string<char> > *' from incompatible type 'const LinkedList<std::__1::basic_string<char> > *'
Why is it I get this error?
next is a T*, and you're trying to assign a const LinkedList<T>* to it.
I suppose you meant something like next = &(otherList.element) (though even then I think your list semantics are somewhat broken — elements shouldn't typically be shared by multiple containers unless you're very, very clear about the ownership semantics).
Contrary to your claims, your first program doesn't work either for the same reason.

c++ doubly linked list with null object model

I'm trying to create a doubly-linked list with the null object model. So far, I've implemented a method to add a node to the beginning of the list and a method to display the node. My problem is that the display function always displays 0. Can anyone point out where I've gone wrong and how to fix it? Also, am I on the right track to correctly implementing the null object model here?
Note: This is a school assignment. Please don't just post a solution without an explanation. I want to learn and understand what's going on here.
Edit: After fixing the display problem, I have another: When calling getHead() or getTail() with a list that is empty or has nodes, it keeps wanting to use self() from the node class, rather than the nullNode class (in the event of an empty list) or elementNode class (in the event of a list with nodes). I'm stuck on how to fix this.
If I print out the addresses of container.getNext() and container (for an empty list), both addresses are the same so shouldn't adding ->self() to the end call the self() method from the nullNode class?
class node {
public:
node(){/* Do nothing */}
node(int e){ element = e; }
int getData(){ return element; }
void setData(int e){ element = e; }
friend class list;
protected:
node* getNext(){ return next; }
void setNext(node* n){ next = n; }
node* getPrev() { return prev; }
void setPrev(node* n){ prev = n; }
node* self();
private:
int element;
node* next;
node* prev;
};
class nullNode : public node{
public:
nullNode(){/* Do nothing */}
int getData(){ return NULL; }
void setData(int e){ /* Do Nothing */ }
node* getNext(){ return head; }
void setNext(node* n){ head = n; }
node* getPrev() { return tail; }
void setPrev(node* n){ tail = n; }
node* self(){ return NULL; }
private:
node* head;
node* tail;
};
class elementNode : public node{
public:
elementNode(){/* Do nothing */}
elementNode(int element){
setData(element);
}
int getData(){ return node::getData(); }
void setData(int e){ node::setData(e); }
node* getNext(){ return node::getNext(); }
void setNext(node* n){ node::setNext(n); }
node* getPrev() { return node::getPrev(); }
void setPrev(node* n){ node::setPrev(n); }
node* self(){ return this; }
};
class list{
public:
list();
node* getHead(){ return (container.getNext())->self(); }
node* getTail(){ return (container.getPrev())->self(); }
node* addHeadNode(int e);
void removeNode(node* n);
void insertBefore(node* n, int e);
void insertAfter(node* n, int e);
void displayNode(node *n);
private:
nullNode container;
};
list::list()
{
container.setNext(&container);
container.setPrev(&container);
}
node* list::addHeadNode(int e)
{
node* foo = new elementNode(e);
foo->setPrev(&container);
foo->setNext(container.getNext());
container.getNext()->setPrev(foo);
container.setNext(foo);
return foo;
}
void list::displayNode(node* n)
{
cout << "Node Data: " << n->getData() << endl;
}
int main()
{
list myList;
node* myNode;
myNode = myList.addHeadNode(5);
myList.displayNode(myNode);
return 0;
}
elementNode(int element)
{
node e;
e.setData(element);
}
What is this code doing? You create node e, but it appears to then be thrown away and not added to any list.
The problem hides in
elementNode(int element){
node e;
e.setData(element);
}
What is going on here? First you create an instance of the node class and then call its setData member function. Sure enough e is modified with the value of element but the very next moment both e and element are vanished out of existence because the scope where they were initialized has ceased to its end (terminated by }) while the information in element hasn't been saved anywhere.
However, if you replace the above code with
elementNode(int element){
setData(element);
}
it calls the inherited setData member function, the value of element is saved, and the program outputs 5 as expected.
Your elementNode constructor is trying to initialize it's node part:
elementNode(int element){
node e;
e.setData(element);
}
You actually just construct an unrelated node then discard it.
What you want is to call your superclass constructor, which can be done in the subclass constructor's initialization list:
elementNode(int element) : node(element) {
}