patterncppMinor
Singly linked list template implementation
Viewed 0 times
templatesinglyimplementationlistlinked
Problem
My implementation is only for study purposes, I agree that some of the features may be counter-intuitive.
I guess the best method is to use a list in the STL library.
Any improvements will be greatly appreciated.
The following code compiles and works, If possible I would like the code reviewed in C++98, If not the closest to him which is C++03;
```
#include
using namespace std;
template
struct node {
node(T data) : data(data), next(NULL) {}
T data;
node *next;
};
template
class linkedlist {
template
friend ostream &operator &rhs);
private:
node *head;
public:
linkedlist() : head(NULL) {};
~linkedlist();
linkedlist(const linkedlist &rhs);
linkedlist &operator=(const linkedlist &rhs);
void insert(const T data);
bool search(const T data) const;
void remove(const T data);
bool isEmpty() const;
bool operator==(const linkedlist &rhs) const;
bool operator!=(const linkedlist &rhs) const;
bool operator &rhs) const;
bool operator>(const linkedlist &rhs) const;
};
template
bool linkedlist::operator!=(const linkedlist &rhs) const
{
return !(*this == rhs);
}
template
bool linkedlist::operator& rhs) const
{
node *lhsTemp = head;
node *rhsTemp = rhs.head;
while (lhsTemp != NULL && rhsTemp != NULL)
{
if (lhsTemp->data > rhsTemp->data)
return false;
lhsTemp = lhsTemp->next;
rhsTemp = rhsTemp->next;
}
return true;
}
template
bool linkedlist::operator>(const linkedlist& rhs) const
{
return rhs
bool linkedlist::operator==(const linkedlist &rhs) const
{
node *lhsTemp = head;
node *rhsTemp = rhs.head;
while (lhsTemp != NULL || rhsTemp != NULL)
{
if (lhsTemp != NULL && rhsTemp == NULL || lhsTemp == NULL && rhsTemp != NULL)
return false;
else if (lhsTemp->data != rhsTemp->data)
return false;
lhsTemp = lhsTemp->next;
rhsTemp = rhsTemp->next;
}
return true;
}
t
I guess the best method is to use a list in the STL library.
Any improvements will be greatly appreciated.
The following code compiles and works, If possible I would like the code reviewed in C++98, If not the closest to him which is C++03;
```
#include
using namespace std;
template
struct node {
node(T data) : data(data), next(NULL) {}
T data;
node *next;
};
template
class linkedlist {
template
friend ostream &operator &rhs);
private:
node *head;
public:
linkedlist() : head(NULL) {};
~linkedlist();
linkedlist(const linkedlist &rhs);
linkedlist &operator=(const linkedlist &rhs);
void insert(const T data);
bool search(const T data) const;
void remove(const T data);
bool isEmpty() const;
bool operator==(const linkedlist &rhs) const;
bool operator!=(const linkedlist &rhs) const;
bool operator &rhs) const;
bool operator>(const linkedlist &rhs) const;
};
template
bool linkedlist::operator!=(const linkedlist &rhs) const
{
return !(*this == rhs);
}
template
bool linkedlist::operator& rhs) const
{
node *lhsTemp = head;
node *rhsTemp = rhs.head;
while (lhsTemp != NULL && rhsTemp != NULL)
{
if (lhsTemp->data > rhsTemp->data)
return false;
lhsTemp = lhsTemp->next;
rhsTemp = rhsTemp->next;
}
return true;
}
template
bool linkedlist::operator>(const linkedlist& rhs) const
{
return rhs
bool linkedlist::operator==(const linkedlist &rhs) const
{
node *lhsTemp = head;
node *rhsTemp = rhs.head;
while (lhsTemp != NULL || rhsTemp != NULL)
{
if (lhsTemp != NULL && rhsTemp == NULL || lhsTemp == NULL && rhsTemp != NULL)
return false;
else if (lhsTemp->data != rhsTemp->data)
return false;
lhsTemp = lhsTemp->next;
rhsTemp = rhsTemp->next;
}
return true;
}
t
Solution
Never use
Next, hide your
Furthermore, you have an inconsistent naming convention. Your variables are written in camelCase, your methods are written in camelCase, but your class is just
It's great that you implemented
Further remarks
Your implementation is fine, but it is. Hm. Not very helpful.
using namespace std in a header. While using namespace std is already considered bad practice in general, it's especially evil in a header file. It affects every file that includes your header.Next, hide your
node. It's an implementation detail. You can put it in a detail namespace, inside your linkedlist class, or in another header. But it shouldn't be readily available to an end-user.Furthermore, you have an inconsistent naming convention. Your variables are written in camelCase, your methods are written in camelCase, but your class is just
linkedlist. The sample size is too small to see how you want to name your methods. However, linkedlist is harder to read than LinkedList, linkedList or linked_list.It's great that you implemented
> in terms of template
void linkedlist::search(const T data) const
{
if (head == NULL)
return false;
for (node *tmp = head; tmp != NULL; tmp = tmp->next)
if (tmp->data == data)
return true;
return true;
}
That won't compile. Or your compiler should yell at you, but it doesn't want to. Make suire that you enable warnings.
Undefined behaviour
In operator=, your for loop must not run if rhs is empty:
template
linkedlist & linkedlist::operator=(const linkedlist &rhs)
{
if (this != &rhs)
{
// ... cut
// let's say rhs.head == NULL
// undefined behaviour, since rhs.head does not point
// to something valid.
for (node *tmp = rhs.head->next; tmp != NULL; tmp = tmp->next)
{
tmpHead->next = new node(tmp->data);
tmpHead = tmpHead->next;
}
}
return *this;
}
Also, you have a nice little function to check whether a list is empty. Use it. And tmpHead is somewhat misleading. You don't have a temporary head, you have a temporary last element or tail. But since we focus on only one node at a time, let's call it current:
template
linkedlist & linkedlist::operator=(const linkedlist &rhs)
{
if (this != &rhs)
{
clear(); // the function at the start of this review
if(rhs.isEmpty()) {
// short cut, since the other list is empty
return *this;
}
head = new node(rhs.head->data);
node * current = head;
for (node *tmp = rhs.head->next; tmp != NULL; tmp = tmp->next)
{
current->next = new node(tmp->data);
current = current->next;
}
}
return *this;
}
By the way, why is it a bad idea at the moment to use insert, although it makes your code a lot easier?
template
linkedlist & linkedlist::operator=(const linkedlist &rhs)
{
if (this != &rhs)
{
clear(); // the function at the start of this review
for(const_node_ptr tmp = rhs.head; tmp != NULL; tmp = tmp->next)
{
insert(tmp->data);
}
}
return *this;
}
This is a lot easier to read, and you can easily check that it's correct. But why is it a bad idea at the moment? And what can you do to change that?
More on that in the last section of the review. By the way, const_node_ptr is
typedef const node_type * const_node_ptr
We have to define it that way, because
const node_ptr == node_type * const
which would mean that your pointer is constant, but not the value it points to.
Further improvements
Your remove is a tad to complex. Don't be afraid to simply return when you're done with the job. And move conditions that should be checked once out of a loop:
template
void linkedlist::remove(const T data)
{
if(isEmpty())
{
return;
}
if (head->data == data)
{
node * tmp = head;
head = head->next;
delete tmp;
return;
}
for (node_ptr curr = head->next, prev = head; curr != NULL; curr = curr->next)
{
if (curr->data == data)
{
node *tmp = curr;
prev->next = curr->next;
delete tmp;
return;
}
prev = curr;
}
}
The function didn't really get shorter, but it's now easier to reason about its behaviour.
Now it's time to talk about insert. You had about a screen page to think about it, which isn't really much time, sorry. But what's the current problem? Why is it a bad idea to use it in operator=?
Because you have to traverse the whole list for every insertion. This would change your current operator= from \$\mathcal O(n)\$ to \$\mathcal O(n^2)\$. So how could you fix this? It's somewhat easy: You have to remember not only the first, but also the last element of the list. A tail.
Note that this will make all functions that remove elements more awkward. Your current implementation works, but if you want to have an \$\mathcal O(1)\$ insert, you really need another pointer in your linkedlist`.Further remarks
Your implementation is fine, but it is. Hm. Not very helpful.
Code Snippets
void clear () {
while(head != NULL) {
node_ptr tmp = head->next;
delete head;
head = tmp;
}
}typedef node<T> node_type;
typedef node_type* node_ptr;head->data = T(); // compiles finetemplate <class T>
bool linkedlist<T>::operator==(const linkedlist<T> &rhs) const
{ //vvvvv
const node<T> *lhsTemp = head;
const node<T> *rhsTemp = rhs.head;
while (lhsTemp != NULL && rhsTemp != NULL)
{
if (lhsTemp->data != rhsTemp->data)
return false;
lhsTemp = lhsTemp->next;
rhsTemp = rhsTemp->next;
}
return (lhsTemp == NULL) && (rhsTemp == NULL);
}template <class T>
bool linkedlist<T>::operator==(const linkedlist<T> &rhs) const
{
if(this == &rhs) {
return true;
}
// other function as above
}Context
StackExchange Code Review Q#155495, answer score: 5
Revisions (0)
No revisions yet.