Yet another doubly linked list

Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
4
down vote
favorite
This is a working but very simple doubly linked list. I wrote it to get more familiar with certain language concepts and because I've never made one in C++. (For convenience reasons this is not split into separate files for declaration/definition)
Things I'm mainly interested in:
Is the overall implementation correct?
Are the various constructors/operators implemented correctly? I'm still not very sure about these so this is an important concern.
How is
constusage?Where, if possible, can
constexprbe used to make this more robust?Is this well written/easy to read? What could be improved to make it better in that regard?
and anything else you notice.
Code:
#include <iostream>
#include <cassert>
#include <initializer_list>
#include <utility>
template<typename T>
class DoublyLinkedList
public:
DoublyLinkedList()
: headnullptr
, tailnullptr
, list_size0
DoublyLinkedList(std::initializer_list<T> init_list)
: DoublyLinkedList
for (auto const& value : init_list)
push_back(value);
DoublyLinkedList(DoublyLinkedList const& rhs)
: DoublyLinkedList
Node* node = rhs.head;
while (node)
push_back(node->data);
node = node->next;
DoublyLinkedList(DoublyLinkedList&& rhs) noexcept
: headrhs.head
, tailrhs.tail
, list_sizerhs.list_size
rhs.head = nullptr;
rhs.tail = nullptr;
rhs.list_size = 0;
~DoublyLinkedList() noexcept
clear();
DoublyLinkedList& operator=(DoublyLinkedList const& rhs)
DoublyLinkedList tmp(rhs);
*this = std::move(tmp);
return *this;
DoublyLinkedList& operator=(DoublyLinkedList&& rhs) noexcept
if (this == &rhs)
return *this;
clear();
head = rhs.head;
tail = rhs.tail;
list_size = rhs.list_size;
rhs.head = nullptr;
rhs.tail = nullptr;
rhs.list_size = 0;
return *this;
bool is_empty() const
return head == nullptr;
int const& size() const
return list_size;
void clear()
Node* node = head;
while (node)
Node* delete_this = node;
node = node->next;
delete delete_this;
head = nullptr;
tail = nullptr;
list_size = 0;
void push_front(T const& value)
if (!head)
head = new Nodenullptr, nullptr, value;
tail = head;
else
head->prev = new Nodehead, nullptr, value;
head = head->prev;
++list_size;
void push_back(T const& value)
if (!tail)
push_front(value);
return;
tail->next = new Nodenullptr, tail, value;
tail = tail->next;
++list_size;
void insert_after(int const& position, T const& value)
int i = 0;
Node* node = head;
while (node)
if (i++ == position)
Node* new_node = new Nodenode->next, node, value;
new_node->next->prev = new_node;
node->next = new_node;
++list_size;
return;
node = node->next;
void erase(int const& position)
if (position <= 0)
pop_front();
return;
if (position >= list_size - 1)
pop_back();
return;
int i = 1;
Node* node = head->next;
while (node)
if (i++ == position)
node->prev->next = node->next;
node->next->prev = node->prev;
delete node;
--list_size;
return;
node = node->next;
void pop_front()
if (head->next)
Node* node = head->next;
delete head;
head = node;
head->prev = nullptr;
else
delete head;
head = nullptr;
tail = nullptr;
--list_size;
void pop_back()
if (tail->prev)
Node* node = tail->prev;
delete tail;
tail = node;
tail->next = nullptr;
else
delete tail;
tail = nullptr;
head = nullptr;
--list_size;
T& front() const
return head->data;
T& back() const
return tail->data;
int find_first_of(int const& start, T const& value) const
private:
struct Node
Node(Node* n, Node* p, T d)
: nextn
, prevp
, datad
Node* next;
Node* prev;
T data;
;
Node* head;
Node* tail;
int list_size;
;
int main()
DoublyLinkedList<int> dll1, 2, 3;
assert(dll.size() == 3);
assert(dll.find_first_of(0, 2) == 1);
dll.erase(1);
assert(dll.size() == 2);
dll.pop_front();
assert(dll.size() == 1);
dll.pop_back();
assert(dll.size() == 0);
assert(dll.is_empty());
dll.push_back(1);
assert(dll.size() == 1);
assert(!dll.is_empty());
dll.push_back(1);
assert(dll.size() == 2);
dll.insert_after(0, 3);
assert(dll.find_first_of(0, 3) == 1);
c++ c++11 linked-list reinventing-the-wheel
add a comment |Â
up vote
4
down vote
favorite
This is a working but very simple doubly linked list. I wrote it to get more familiar with certain language concepts and because I've never made one in C++. (For convenience reasons this is not split into separate files for declaration/definition)
Things I'm mainly interested in:
Is the overall implementation correct?
Are the various constructors/operators implemented correctly? I'm still not very sure about these so this is an important concern.
How is
constusage?Where, if possible, can
constexprbe used to make this more robust?Is this well written/easy to read? What could be improved to make it better in that regard?
and anything else you notice.
Code:
#include <iostream>
#include <cassert>
#include <initializer_list>
#include <utility>
template<typename T>
class DoublyLinkedList
public:
DoublyLinkedList()
: headnullptr
, tailnullptr
, list_size0
DoublyLinkedList(std::initializer_list<T> init_list)
: DoublyLinkedList
for (auto const& value : init_list)
push_back(value);
DoublyLinkedList(DoublyLinkedList const& rhs)
: DoublyLinkedList
Node* node = rhs.head;
while (node)
push_back(node->data);
node = node->next;
DoublyLinkedList(DoublyLinkedList&& rhs) noexcept
: headrhs.head
, tailrhs.tail
, list_sizerhs.list_size
rhs.head = nullptr;
rhs.tail = nullptr;
rhs.list_size = 0;
~DoublyLinkedList() noexcept
clear();
DoublyLinkedList& operator=(DoublyLinkedList const& rhs)
DoublyLinkedList tmp(rhs);
*this = std::move(tmp);
return *this;
DoublyLinkedList& operator=(DoublyLinkedList&& rhs) noexcept
if (this == &rhs)
return *this;
clear();
head = rhs.head;
tail = rhs.tail;
list_size = rhs.list_size;
rhs.head = nullptr;
rhs.tail = nullptr;
rhs.list_size = 0;
return *this;
bool is_empty() const
return head == nullptr;
int const& size() const
return list_size;
void clear()
Node* node = head;
while (node)
Node* delete_this = node;
node = node->next;
delete delete_this;
head = nullptr;
tail = nullptr;
list_size = 0;
void push_front(T const& value)
if (!head)
head = new Nodenullptr, nullptr, value;
tail = head;
else
head->prev = new Nodehead, nullptr, value;
head = head->prev;
++list_size;
void push_back(T const& value)
if (!tail)
push_front(value);
return;
tail->next = new Nodenullptr, tail, value;
tail = tail->next;
++list_size;
void insert_after(int const& position, T const& value)
int i = 0;
Node* node = head;
while (node)
if (i++ == position)
Node* new_node = new Nodenode->next, node, value;
new_node->next->prev = new_node;
node->next = new_node;
++list_size;
return;
node = node->next;
void erase(int const& position)
if (position <= 0)
pop_front();
return;
if (position >= list_size - 1)
pop_back();
return;
int i = 1;
Node* node = head->next;
while (node)
if (i++ == position)
node->prev->next = node->next;
node->next->prev = node->prev;
delete node;
--list_size;
return;
node = node->next;
void pop_front()
if (head->next)
Node* node = head->next;
delete head;
head = node;
head->prev = nullptr;
else
delete head;
head = nullptr;
tail = nullptr;
--list_size;
void pop_back()
if (tail->prev)
Node* node = tail->prev;
delete tail;
tail = node;
tail->next = nullptr;
else
delete tail;
tail = nullptr;
head = nullptr;
--list_size;
T& front() const
return head->data;
T& back() const
return tail->data;
int find_first_of(int const& start, T const& value) const
private:
struct Node
Node(Node* n, Node* p, T d)
: nextn
, prevp
, datad
Node* next;
Node* prev;
T data;
;
Node* head;
Node* tail;
int list_size;
;
int main()
DoublyLinkedList<int> dll1, 2, 3;
assert(dll.size() == 3);
assert(dll.find_first_of(0, 2) == 1);
dll.erase(1);
assert(dll.size() == 2);
dll.pop_front();
assert(dll.size() == 1);
dll.pop_back();
assert(dll.size() == 0);
assert(dll.is_empty());
dll.push_back(1);
assert(dll.size() == 1);
assert(!dll.is_empty());
dll.push_back(1);
assert(dll.size() == 2);
dll.insert_after(0, 3);
assert(dll.find_first_of(0, 3) == 1);
c++ c++11 linked-list reinventing-the-wheel
add a comment |Â
up vote
4
down vote
favorite
up vote
4
down vote
favorite
This is a working but very simple doubly linked list. I wrote it to get more familiar with certain language concepts and because I've never made one in C++. (For convenience reasons this is not split into separate files for declaration/definition)
Things I'm mainly interested in:
Is the overall implementation correct?
Are the various constructors/operators implemented correctly? I'm still not very sure about these so this is an important concern.
How is
constusage?Where, if possible, can
constexprbe used to make this more robust?Is this well written/easy to read? What could be improved to make it better in that regard?
and anything else you notice.
Code:
#include <iostream>
#include <cassert>
#include <initializer_list>
#include <utility>
template<typename T>
class DoublyLinkedList
public:
DoublyLinkedList()
: headnullptr
, tailnullptr
, list_size0
DoublyLinkedList(std::initializer_list<T> init_list)
: DoublyLinkedList
for (auto const& value : init_list)
push_back(value);
DoublyLinkedList(DoublyLinkedList const& rhs)
: DoublyLinkedList
Node* node = rhs.head;
while (node)
push_back(node->data);
node = node->next;
DoublyLinkedList(DoublyLinkedList&& rhs) noexcept
: headrhs.head
, tailrhs.tail
, list_sizerhs.list_size
rhs.head = nullptr;
rhs.tail = nullptr;
rhs.list_size = 0;
~DoublyLinkedList() noexcept
clear();
DoublyLinkedList& operator=(DoublyLinkedList const& rhs)
DoublyLinkedList tmp(rhs);
*this = std::move(tmp);
return *this;
DoublyLinkedList& operator=(DoublyLinkedList&& rhs) noexcept
if (this == &rhs)
return *this;
clear();
head = rhs.head;
tail = rhs.tail;
list_size = rhs.list_size;
rhs.head = nullptr;
rhs.tail = nullptr;
rhs.list_size = 0;
return *this;
bool is_empty() const
return head == nullptr;
int const& size() const
return list_size;
void clear()
Node* node = head;
while (node)
Node* delete_this = node;
node = node->next;
delete delete_this;
head = nullptr;
tail = nullptr;
list_size = 0;
void push_front(T const& value)
if (!head)
head = new Nodenullptr, nullptr, value;
tail = head;
else
head->prev = new Nodehead, nullptr, value;
head = head->prev;
++list_size;
void push_back(T const& value)
if (!tail)
push_front(value);
return;
tail->next = new Nodenullptr, tail, value;
tail = tail->next;
++list_size;
void insert_after(int const& position, T const& value)
int i = 0;
Node* node = head;
while (node)
if (i++ == position)
Node* new_node = new Nodenode->next, node, value;
new_node->next->prev = new_node;
node->next = new_node;
++list_size;
return;
node = node->next;
void erase(int const& position)
if (position <= 0)
pop_front();
return;
if (position >= list_size - 1)
pop_back();
return;
int i = 1;
Node* node = head->next;
while (node)
if (i++ == position)
node->prev->next = node->next;
node->next->prev = node->prev;
delete node;
--list_size;
return;
node = node->next;
void pop_front()
if (head->next)
Node* node = head->next;
delete head;
head = node;
head->prev = nullptr;
else
delete head;
head = nullptr;
tail = nullptr;
--list_size;
void pop_back()
if (tail->prev)
Node* node = tail->prev;
delete tail;
tail = node;
tail->next = nullptr;
else
delete tail;
tail = nullptr;
head = nullptr;
--list_size;
T& front() const
return head->data;
T& back() const
return tail->data;
int find_first_of(int const& start, T const& value) const
private:
struct Node
Node(Node* n, Node* p, T d)
: nextn
, prevp
, datad
Node* next;
Node* prev;
T data;
;
Node* head;
Node* tail;
int list_size;
;
int main()
DoublyLinkedList<int> dll1, 2, 3;
assert(dll.size() == 3);
assert(dll.find_first_of(0, 2) == 1);
dll.erase(1);
assert(dll.size() == 2);
dll.pop_front();
assert(dll.size() == 1);
dll.pop_back();
assert(dll.size() == 0);
assert(dll.is_empty());
dll.push_back(1);
assert(dll.size() == 1);
assert(!dll.is_empty());
dll.push_back(1);
assert(dll.size() == 2);
dll.insert_after(0, 3);
assert(dll.find_first_of(0, 3) == 1);
c++ c++11 linked-list reinventing-the-wheel
This is a working but very simple doubly linked list. I wrote it to get more familiar with certain language concepts and because I've never made one in C++. (For convenience reasons this is not split into separate files for declaration/definition)
Things I'm mainly interested in:
Is the overall implementation correct?
Are the various constructors/operators implemented correctly? I'm still not very sure about these so this is an important concern.
How is
constusage?Where, if possible, can
constexprbe used to make this more robust?Is this well written/easy to read? What could be improved to make it better in that regard?
and anything else you notice.
Code:
#include <iostream>
#include <cassert>
#include <initializer_list>
#include <utility>
template<typename T>
class DoublyLinkedList
public:
DoublyLinkedList()
: headnullptr
, tailnullptr
, list_size0
DoublyLinkedList(std::initializer_list<T> init_list)
: DoublyLinkedList
for (auto const& value : init_list)
push_back(value);
DoublyLinkedList(DoublyLinkedList const& rhs)
: DoublyLinkedList
Node* node = rhs.head;
while (node)
push_back(node->data);
node = node->next;
DoublyLinkedList(DoublyLinkedList&& rhs) noexcept
: headrhs.head
, tailrhs.tail
, list_sizerhs.list_size
rhs.head = nullptr;
rhs.tail = nullptr;
rhs.list_size = 0;
~DoublyLinkedList() noexcept
clear();
DoublyLinkedList& operator=(DoublyLinkedList const& rhs)
DoublyLinkedList tmp(rhs);
*this = std::move(tmp);
return *this;
DoublyLinkedList& operator=(DoublyLinkedList&& rhs) noexcept
if (this == &rhs)
return *this;
clear();
head = rhs.head;
tail = rhs.tail;
list_size = rhs.list_size;
rhs.head = nullptr;
rhs.tail = nullptr;
rhs.list_size = 0;
return *this;
bool is_empty() const
return head == nullptr;
int const& size() const
return list_size;
void clear()
Node* node = head;
while (node)
Node* delete_this = node;
node = node->next;
delete delete_this;
head = nullptr;
tail = nullptr;
list_size = 0;
void push_front(T const& value)
if (!head)
head = new Nodenullptr, nullptr, value;
tail = head;
else
head->prev = new Nodehead, nullptr, value;
head = head->prev;
++list_size;
void push_back(T const& value)
if (!tail)
push_front(value);
return;
tail->next = new Nodenullptr, tail, value;
tail = tail->next;
++list_size;
void insert_after(int const& position, T const& value)
int i = 0;
Node* node = head;
while (node)
if (i++ == position)
Node* new_node = new Nodenode->next, node, value;
new_node->next->prev = new_node;
node->next = new_node;
++list_size;
return;
node = node->next;
void erase(int const& position)
if (position <= 0)
pop_front();
return;
if (position >= list_size - 1)
pop_back();
return;
int i = 1;
Node* node = head->next;
while (node)
if (i++ == position)
node->prev->next = node->next;
node->next->prev = node->prev;
delete node;
--list_size;
return;
node = node->next;
void pop_front()
if (head->next)
Node* node = head->next;
delete head;
head = node;
head->prev = nullptr;
else
delete head;
head = nullptr;
tail = nullptr;
--list_size;
void pop_back()
if (tail->prev)
Node* node = tail->prev;
delete tail;
tail = node;
tail->next = nullptr;
else
delete tail;
tail = nullptr;
head = nullptr;
--list_size;
T& front() const
return head->data;
T& back() const
return tail->data;
int find_first_of(int const& start, T const& value) const
private:
struct Node
Node(Node* n, Node* p, T d)
: nextn
, prevp
, datad
Node* next;
Node* prev;
T data;
;
Node* head;
Node* tail;
int list_size;
;
int main()
DoublyLinkedList<int> dll1, 2, 3;
assert(dll.size() == 3);
assert(dll.find_first_of(0, 2) == 1);
dll.erase(1);
assert(dll.size() == 2);
dll.pop_front();
assert(dll.size() == 1);
dll.pop_back();
assert(dll.size() == 0);
assert(dll.is_empty());
dll.push_back(1);
assert(dll.size() == 1);
assert(!dll.is_empty());
dll.push_back(1);
assert(dll.size() == 2);
dll.insert_after(0, 3);
assert(dll.find_first_of(0, 3) == 1);
c++ c++11 linked-list reinventing-the-wheel
asked Apr 3 at 21:27
yuri
3,3862832
3,3862832
add a comment |Â
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
3
down vote
accepted
push_frontcan be streamlined. The new node will become a head no matter what, and itsnextwill point the old head no matter what, so considervoid push_front(T const& value)
node = new Nodehead, nullptr, value;
if (!head)
tail = node;
else
head->prev = node;
head = node;
++list_size;(Same applies to
push_back).Same technique applies to
pop_frontandpop_back, e.g.:void pop_front()
Node * node = head;
head = head->next;
delete node;
if (head)
head->prev = nullptr;
else
tail = nullptr;
--list_size;I don't see the value of
push_backcallingpush_front.OTOH,
clearshould callpop_front, just like constructors callpush_back.Using
intas a position is dangerous and limiting. Considersize_t.insert_after,erase, andfind_first_ofwould benefit from theprivate Node * at(size_t position)utility method.The list sorely misses iterators. Once they are implemented, an entire
<algorithm>library is to your service for free.I don't follow why an
intparameter is passed by a reference.
I looked at several ways to implement STL compatible iterators none of them were complete. Do you happen to have a good introduction to this topic?
â yuri
Apr 5 at 20:52
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
accepted
push_frontcan be streamlined. The new node will become a head no matter what, and itsnextwill point the old head no matter what, so considervoid push_front(T const& value)
node = new Nodehead, nullptr, value;
if (!head)
tail = node;
else
head->prev = node;
head = node;
++list_size;(Same applies to
push_back).Same technique applies to
pop_frontandpop_back, e.g.:void pop_front()
Node * node = head;
head = head->next;
delete node;
if (head)
head->prev = nullptr;
else
tail = nullptr;
--list_size;I don't see the value of
push_backcallingpush_front.OTOH,
clearshould callpop_front, just like constructors callpush_back.Using
intas a position is dangerous and limiting. Considersize_t.insert_after,erase, andfind_first_ofwould benefit from theprivate Node * at(size_t position)utility method.The list sorely misses iterators. Once they are implemented, an entire
<algorithm>library is to your service for free.I don't follow why an
intparameter is passed by a reference.
I looked at several ways to implement STL compatible iterators none of them were complete. Do you happen to have a good introduction to this topic?
â yuri
Apr 5 at 20:52
add a comment |Â
up vote
3
down vote
accepted
push_frontcan be streamlined. The new node will become a head no matter what, and itsnextwill point the old head no matter what, so considervoid push_front(T const& value)
node = new Nodehead, nullptr, value;
if (!head)
tail = node;
else
head->prev = node;
head = node;
++list_size;(Same applies to
push_back).Same technique applies to
pop_frontandpop_back, e.g.:void pop_front()
Node * node = head;
head = head->next;
delete node;
if (head)
head->prev = nullptr;
else
tail = nullptr;
--list_size;I don't see the value of
push_backcallingpush_front.OTOH,
clearshould callpop_front, just like constructors callpush_back.Using
intas a position is dangerous and limiting. Considersize_t.insert_after,erase, andfind_first_ofwould benefit from theprivate Node * at(size_t position)utility method.The list sorely misses iterators. Once they are implemented, an entire
<algorithm>library is to your service for free.I don't follow why an
intparameter is passed by a reference.
I looked at several ways to implement STL compatible iterators none of them were complete. Do you happen to have a good introduction to this topic?
â yuri
Apr 5 at 20:52
add a comment |Â
up vote
3
down vote
accepted
up vote
3
down vote
accepted
push_frontcan be streamlined. The new node will become a head no matter what, and itsnextwill point the old head no matter what, so considervoid push_front(T const& value)
node = new Nodehead, nullptr, value;
if (!head)
tail = node;
else
head->prev = node;
head = node;
++list_size;(Same applies to
push_back).Same technique applies to
pop_frontandpop_back, e.g.:void pop_front()
Node * node = head;
head = head->next;
delete node;
if (head)
head->prev = nullptr;
else
tail = nullptr;
--list_size;I don't see the value of
push_backcallingpush_front.OTOH,
clearshould callpop_front, just like constructors callpush_back.Using
intas a position is dangerous and limiting. Considersize_t.insert_after,erase, andfind_first_ofwould benefit from theprivate Node * at(size_t position)utility method.The list sorely misses iterators. Once they are implemented, an entire
<algorithm>library is to your service for free.I don't follow why an
intparameter is passed by a reference.
push_frontcan be streamlined. The new node will become a head no matter what, and itsnextwill point the old head no matter what, so considervoid push_front(T const& value)
node = new Nodehead, nullptr, value;
if (!head)
tail = node;
else
head->prev = node;
head = node;
++list_size;(Same applies to
push_back).Same technique applies to
pop_frontandpop_back, e.g.:void pop_front()
Node * node = head;
head = head->next;
delete node;
if (head)
head->prev = nullptr;
else
tail = nullptr;
--list_size;I don't see the value of
push_backcallingpush_front.OTOH,
clearshould callpop_front, just like constructors callpush_back.Using
intas a position is dangerous and limiting. Considersize_t.insert_after,erase, andfind_first_ofwould benefit from theprivate Node * at(size_t position)utility method.The list sorely misses iterators. Once they are implemented, an entire
<algorithm>library is to your service for free.I don't follow why an
intparameter is passed by a reference.
answered Apr 3 at 23:37
vnp
36.5k12991
36.5k12991
I looked at several ways to implement STL compatible iterators none of them were complete. Do you happen to have a good introduction to this topic?
â yuri
Apr 5 at 20:52
add a comment |Â
I looked at several ways to implement STL compatible iterators none of them were complete. Do you happen to have a good introduction to this topic?
â yuri
Apr 5 at 20:52
I looked at several ways to implement STL compatible iterators none of them were complete. Do you happen to have a good introduction to this topic?
â yuri
Apr 5 at 20:52
I looked at several ways to implement STL compatible iterators none of them were complete. Do you happen to have a good introduction to this topic?
â yuri
Apr 5 at 20:52
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f191201%2fyet-another-doubly-linked-list%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password