Skip to content

Commit

Permalink
完成20.4节双向链表v4,增加const_iterator、拷贝操作、移动操作、assign()和splice()
Browse files Browse the repository at this point in the history
  • Loading branch information
ZZy979 committed Feb 16, 2024
1 parent d08265f commit 73b4b53
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 11 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ $ ctest
* [Jack-and-Jill v3](ch20/jack_and_jill_v3.cpp)

### 20.4 链表
* [双向链表v4](ch20/doubly_linked_list_v4.h) (简化的`std::list`[WIP]
* [双向链表v4](ch20/doubly_linked_list_v4.h) (简化的`std::list`

### 20.5 再次一般化vector
* [简单向量v3](ch19/simple_vector.h)
Expand Down
175 changes: 165 additions & 10 deletions ch20/doubly_linked_list_v4.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ struct Node_base {
Node_base* succ; // successor (next) link

void insert(Node_base* n);
void insert(Node_base* first, Node_base* last);
void erase();
};

Expand All @@ -20,6 +21,20 @@ void Node_base::insert(Node_base* n) {
prev = n;
}

// insert [first, last) before this node
void Node_base::insert(Node_base* first, Node_base* last) {
if (this != last) {
last->prev->succ = this;
first->prev->succ = last;
prev->succ = first;

auto tmp = prev;
prev = last->prev;
last->prev = first->prev;
first->prev = tmp;
}
}

// remove this node from list
void Node_base::erase() {
if (succ) succ->prev = prev;
Expand Down Expand Up @@ -48,54 +63,110 @@ struct List_iterator {
List_iterator& operator++() { node = node->succ; return *this; } // forward
List_iterator& operator--() { node = node->prev; return *this; } // backward

T& operator*() { return static_cast<Link<T>*>(node)->val; } // get value (dereference)
T* operator->() { return &static_cast<Link<T>*>(node)->val; }
reference operator*() const { return static_cast<Link<T>*>(node)->val; } // get value (dereference)
pointer operator->() const { return &static_cast<Link<T>*>(node)->val; }

bool operator==(const List_iterator& b) const { return node == b.node; }
bool operator!=(const List_iterator& b) const { return node != b.node; }

Node_base* node; // current link
};

// doubly-linked list const iterator
template<class T>
struct List_const_iterator {
using iterator_category = std::bidirectional_iterator_tag;
using value_type = T;
using difference_type = ptrdiff_t;
using pointer = const T*;
using reference = const T&;

explicit List_const_iterator(const Node_base* p) :node(p) {}

List_const_iterator& operator++() { node = node->succ; return *this; } // forward
List_const_iterator& operator--() { node = node->prev; return *this; } // backward

reference operator*() const { return static_cast<const Link<T>*>(node)->val; } // get value (dereference)
pointer operator->() const { return &static_cast<const Link<T>*>(node)->val; }

bool operator==(const List_const_iterator& b) const { return node == b.node; }
bool operator!=(const List_const_iterator& b) const { return node != b.node; }

const Node_base* node; // current link
};

template<class Iter, class Tag>
using is_iterator = std::is_convertible<typename std::iterator_traits<Iter>::iterator_category, Tag>;

template<class Iter>
using is_input_iterator = is_iterator<Iter, std::input_iterator_tag>;

// doubly-linked list
template<class T>
class list {
public:
using size_type = size_t;
using value_type = T;
using iterator = List_iterator<T>;
// TODO const_iterator
using const_iterator = List_const_iterator<T>;

list() { _init(); }

explicit list(size_type n, const T& val = T()) {
_init();
for (int i = 0; i < n; ++i) push_back(val);
for (size_type i = 0; i < n; ++i) push_back(val);
}

template<class InputIt, typename std::enable_if<std::is_convertible<
typename std::iterator_traits<InputIt>::iterator_category, std::input_iterator_tag>::value, int>::type = 0>
template<class InputIt, class = std::enable_if_t<is_input_iterator<InputIt>::value>>
list(InputIt first, InputIt last) {
_init();
for (; first != last; ++first) push_back(*first);
}

list(std::initializer_list<T> lst) :list(lst.begin(), lst.end()) {}

~list() { _clear(); }
list(const list& l) :list(l.begin(), l.end()) {}

// TODO 拷贝构造、拷贝赋值、移动构造、移动赋值
list& operator=(const list& l) {
if (this != &l) assign(l.begin(), l.end());
return *this;
}

list(list&& l) noexcept { _move(std::move(l)); }

list& operator=(list&& l) noexcept {
_clear();
_move(std::move(l));
return *this;
}

~list() { _clear(); }

// iterator to first element
iterator begin() { return iterator(head.succ); }
const_iterator begin() const { return const_iterator(head.succ); }

// iterator to one beyond last element
iterator end() { return iterator(&head); }
const_iterator end() const { return const_iterator(&head); }

iterator insert(iterator p, const T& v);
iterator insert(iterator p, size_type n, const T& val);

template<class InputIt, class = std::enable_if_t<is_input_iterator<InputIt>::value>>
iterator insert(iterator p, InputIt first, InputIt last);

iterator erase(iterator p);
iterator erase(iterator first, iterator last);

void clear() { _clear(); _init(); }

void assign(size_type n, const T& val);

// replace the contents with [first, last)
template<class InputIt, class = std::enable_if_t<is_input_iterator<InputIt>::value>>
void assign(InputIt first, InputIt last);

// insert v at end
void push_back(const T& v) { insert(end(), v); }

Expand All @@ -110,18 +181,25 @@ class list {

// the first element
T& front() { return *begin(); }
const T& front() const { return *begin(); }

// the last element
T& back() { return *std::prev(end()); }
const T& back() const { return *std::prev(end()); }

size_type size() const { return sz; }
bool empty() const { return sz == 0; }

int size() const { return sz; }
void splice(iterator p, list& l) { splice(p, l, l.begin(), l.end()); }
void splice(iterator p, list& l, iterator first, iterator last);

private:
void _init();
void _clear();
void _move(list&& l);

Node_base head; // virtual head node
int sz; // number of elements
size_type sz; // number of elements
};

// insert v into list before p
Expand All @@ -133,6 +211,27 @@ typename list<T>::iterator list<T>::insert(iterator p, const T& v) {
return iterator(tmp);
}

// insert n copies of val into list before p
template<class T>
typename list<T>::iterator list<T>::insert(iterator p, size_type n, const T& val) {
if (n == 0) return p;
list<T> tmp(n, val);
iterator it = tmp.begin();
splice(p, tmp);
return it;
}

// insert [first, last) into list before p
template<class T>
template<class InputIt, class>
typename list<T>::iterator list<T>::insert(iterator p, InputIt first, InputIt last) {
if (first == last) return p;
list<T> tmp(first, last);
iterator it = tmp.begin();
splice(p, tmp);
return it;
}

// remove p from the list
template<class T>
typename list<T>::iterator list<T>::erase(iterator p) {
Expand All @@ -144,6 +243,49 @@ typename list<T>::iterator list<T>::erase(iterator p) {
return iterator(next);
}

// remove [first, last) from the list
template<class T>
typename list<T>::iterator list<T>::erase(iterator first, iterator last) {
while (first != last)
first = erase(first);
return last;
}

// replace the contents with n copies of val
template<class T>
void list<T>::assign(size_type n, const T& val) {
iterator it = begin();
for (; it != end() && n > 0; ++it, --n)
*it = val;
if (n > 0)
insert(end(), n, val);
else
erase(it, end());
}

template<class T>
template<class InputIt, class>
void list<T>::assign(InputIt first2, InputIt last2) {
iterator first1 = begin(), last1 = end();
for (; first1 != last1 && first2 != last2; ++first1, ++first2)
*first1 = *first2;
if (first2 == last2)
erase(first1, last1);
else
insert(last1, first2, last2);
}

// transfer elements in [first, last) from l to this list, insert before p
template<class T>
void list<T>::splice(iterator p, list& l, iterator first, iterator last) {
if (first != last) {
size_t n = std::distance(first, last);
p.node->insert(first.node, last.node);
sz += n;
l.sz -= n;
}
}

template<class T>
void list<T>::_init() {
head.prev = head.succ = &head;
Expand All @@ -159,3 +301,16 @@ void list<T>::_clear() {
p = next;
}
}

template<class T>
void list<T>::_move(list&& l) {
if (l.empty()) {
_init();
}
else {
head = l.head;
head.succ->prev = head.prev->succ = &head;
sz = l.sz;
l._init();
}
}
71 changes: 71 additions & 0 deletions ch20/doubly_linked_list_v4_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,77 @@ TEST_F(DoublyLinkedListV4Test, InitializerListConstructor) {
EXPECT_EQ(Counted_element::count(), 3);
}

TEST_F(DoublyLinkedListV4Test, CopyConstructor) {
list<double> l2 = l_;
EXPECT_EQ(l2.size(), l_.size());
EXPECT_TRUE(std::equal(l2.begin(), l2.end(), l_.begin()));
*std::next(l_.begin()) = 99;
l2.front() = 88;
EXPECT_DOUBLE_EQ(l_.front(), 0);
EXPECT_DOUBLE_EQ(*std::next(l2.begin()), 1.1);

// class with resources
list<Counted_element> le(5);
EXPECT_EQ(Counted_element::count(), 5);
list<Counted_element> le2 = le;
EXPECT_EQ(Counted_element::count(), 10);
}

TEST_F(DoublyLinkedListV4Test, CopyAssignment) {
// self-assignment
l_ = l_;
double expected[] = {0.0, 1.1, 2.2, 3.3, 4.4};
EXPECT_EQ(l_.size(), 5);
EXPECT_TRUE(std::equal(l_.begin(), l_.end(), expected));

list<double> l2 = {1, 2, 3};
l2 = l_;
EXPECT_EQ(l2.size(), l_.size());
for (int i = 0; i < l2.size(); ++i)
EXPECT_TRUE(std::equal(l2.begin(), l2.end(), l_.begin()));
*std::next(l_.begin()) = 99;
l2.front() = 88;
EXPECT_DOUBLE_EQ(l_.front(), 0);
EXPECT_DOUBLE_EQ(*std::next(l2.begin()), 1.1);

// class with resources
list<Counted_element> le(5), le2(8);
EXPECT_EQ(Counted_element::count(), 13);
le2 = le;
EXPECT_EQ(Counted_element::count(), 10);
}

TEST_F(DoublyLinkedListV4Test, MoveConstructor) {
int size = l_.size();
list<double> l2 = std::move(l_);
double expected[] = {0.0, 1.1, 2.2, 3.3, 4.4};
EXPECT_EQ(l2.size(), size);
EXPECT_TRUE(std::equal(l2.begin(), l2.end(), expected));
EXPECT_EQ(l_.size(), 0);

// class with resources
list<Counted_element> le(5);
EXPECT_EQ(Counted_element::count(), 5);
list<Counted_element> le2 = std::move(le);
EXPECT_EQ(Counted_element::count(), 5);
}

TEST_F(DoublyLinkedListV4Test, MoveAssignment) {
list<double> l2 = {1, 2, 3};
int size = l_.size();
l2 = std::move(l_);
double expected[] = {0.0, 1.1, 2.2, 3.3, 4.4};
EXPECT_EQ(l2.size(), size);
EXPECT_TRUE(std::equal(l2.begin(), l2.end(), expected));
EXPECT_EQ(l_.size(), 0);

// class with resources
list<Counted_element> le(5), le2(8);
EXPECT_EQ(Counted_element::count(), 13);
le2 = std::move(le);
EXPECT_EQ(Counted_element::count(), 5);
}

TEST_F(DoublyLinkedListV4Test, Destructor) {
// class with resources
auto ple = new list<Counted_element>(8);
Expand Down

0 comments on commit 73b4b53

Please sign in to comment.