From e3078bca86e1f0e593d8e57485a2664158eb09e4 Mon Sep 17 00:00:00 2001 From: ZZy979 <979481894@qq.com> Date: Sat, 17 Feb 2024 15:55:08 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E4=B9=A0=E9=A2=9820-14?= =?UTF-8?q?=E5=8D=95=E5=90=91=E9=93=BE=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- ch20/CMakeLists.txt | 2 +- ch20/doubly_linked_list_v4.h | 26 +-- ch20/doubly_linked_list_v4_test.cpp | 3 +- ch20/singly_linked_list.h | 287 ++++++++++++++++++++++++---- ch20/singly_linked_list_test.cpp | 247 ++++++++++++++++++++++++ 6 files changed, 510 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 98b7ce1..f994a50 100644 --- a/README.md +++ b/README.md @@ -537,7 +537,7 @@ $ ctest * [20-8~20-10](ch20/simple_text_editor.cpp) * [20-12](ch20/exec20-12.cpp) * [20-13](ch20/doubly_linked_list_v4.h) -* [20-14](ch20/singly_linked_list.h) (简化的`std::forward_list`)[WIP] +* [20-14](ch20/singly_linked_list.h) (简化的`std::forward_list`) * [20-15](ch20/pvector.h) * [20-16](ch20/ovector.h) * [20-17](ch20/ownership_vector.h) diff --git a/ch20/CMakeLists.txt b/ch20/CMakeLists.txt index 61da5df..da0c541 100644 --- a/ch20/CMakeLists.txt +++ b/ch20/CMakeLists.txt @@ -49,7 +49,7 @@ add_output_test( COMMAND exec20-12 OUTPUT_FILE ${TESTDATA_DIR}/exec20-12_output_empty.txt ) -add_gtest(NAME singly_linked_list_test SOURCE singly_linked_list_test.cpp LIBRARY singly_linked_list) +add_gtest(NAME singly_linked_list_test SOURCE singly_linked_list_test.cpp LIBRARY singly_linked_list counted_element) add_gtest(NAME pvector_test SOURCE pvector_test.cpp LIBRARY pvector counted_element) add_gtest(NAME ovector_test SOURCE ovector_test.cpp LIBRARY ovector counted_element) add_gtest(NAME ownership_vector_test SOURCE ownership_vector_test.cpp LIBRARY ownership_vector counted_element) diff --git a/ch20/doubly_linked_list_v4.h b/ch20/doubly_linked_list_v4.h index 6123e42..37087d2 100644 --- a/ch20/doubly_linked_list_v4.h +++ b/ch20/doubly_linked_list_v4.h @@ -163,7 +163,6 @@ class list { void assign(size_type n, const T& val); - // replace the contents with [first, last) template::value>> void assign(InputIt first, InputIt last); @@ -254,25 +253,26 @@ typename list::iterator list::erase(iterator first, iterator last) { // replace the contents with n copies of val template void list::assign(size_type n, const T& val) { - iterator it = begin(); - for (; it != end() && n > 0; ++it, --n) - *it = val; + iterator curr = begin(), end_ = end(); + for (; curr != end_ && n > 0; ++curr, --n) + *curr = val; if (n > 0) - insert(end(), n, val); + insert(end_, n, val); else - erase(it, end()); + erase(curr, end_); } +// replace the contents with [first, last) template template -void list::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); +void list::assign(InputIt first, InputIt last) { + iterator curr = begin(), end_ = end(); + for (; curr != end_ && first != last; ++curr, ++first) + *curr = *first; + if (first == last) + erase(curr, end_); else - insert(last1, first2, last2); + insert(end_, first, last); } // transfer elements in [first, last) from l to this list, insert before p diff --git a/ch20/doubly_linked_list_v4_test.cpp b/ch20/doubly_linked_list_v4_test.cpp index 42fd63e..3912de6 100644 --- a/ch20/doubly_linked_list_v4_test.cpp +++ b/ch20/doubly_linked_list_v4_test.cpp @@ -76,8 +76,7 @@ TEST_F(DoublyLinkedListV4Test, CopyAssignment) { list 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())); + EXPECT_TRUE(std::equal(l2.begin(), l2.end(), l_.begin())); *std::next(l_.begin()) = 99; l2.front() = 88; EXPECT_DOUBLE_EQ(l_.front(), 0); diff --git a/ch20/singly_linked_list.h b/ch20/singly_linked_list.h index 7b70920..96f3cfe 100644 --- a/ch20/singly_linked_list.h +++ b/ch20/singly_linked_list.h @@ -1,66 +1,273 @@ #pragma once +#include +#include +#include + +struct Node_base { + Node_base* succ = nullptr; + + void insert_after(Node_base* n); + void insert_after(Node_base* first, Node_base* last); + void erase_after(); +}; + +// insert n after this node +void Node_base::insert_after(Node_base* n) { + n->succ = succ; + succ = n; +} + +// insert (first, last] after this node +void Node_base::insert_after(Node_base* first, Node_base* last) { + auto tmp = first->succ; + if (last) { + first->succ = last->succ; + last->succ = succ; + } + else { + first->succ = nullptr; + } + succ = tmp; +} + +// remove next node from list +void Node_base::erase_after() { + succ = succ->succ; +} + // singly-linked list node -template -struct Link { - Link* succ; - Elem val; +template +struct Link : public Node_base { + T val; + + explicit Link(const T& v = T()) :Node_base{}, val(v) {} +}; + +// singly-linked list iterator +template +struct Slist_iterator { + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; + + explicit Slist_iterator(Node_base* p = nullptr) :node(p) {} + + Slist_iterator& operator++() { node = node->succ; return *this; } + + reference operator*() const { return static_cast*>(node)->val; } + pointer operator->() const { return &static_cast*>(node)->val; } + + bool operator==(const Slist_iterator& b) const { return node == b.node; } + bool operator!=(const Slist_iterator& b) const { return node != b.node; } + + Node_base* node; // current link }; +// singly-linked list const iterator +template +struct Slist_const_iterator { + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = const T*; + using reference = const T&; + + explicit Slist_const_iterator(const Node_base* p = nullptr) :node(p) {} + + Slist_const_iterator& operator++() { node = node->succ; return *this; } + + reference operator*() const { return static_cast*>(node)->val; } + pointer operator->() const { return &static_cast*>(node)->val; } + + bool operator==(const Slist_const_iterator& b) const { return node == b.node; } + bool operator!=(const Slist_const_iterator& b) const { return node != b.node; } + + const Node_base* node; // current link +}; + +template +using is_iterator = std::is_convertible::iterator_category, Tag>; + +template +using is_input_iterator = is_iterator; + // singly-linked list -template +template class slist { - class iterator; +public: + using size_type = size_t; + using value_type = T; + using iterator = Slist_iterator; + using const_iterator = Slist_const_iterator; + + slist() = default; + + explicit slist(size_type n, const T& val = T()) { + for (auto p = &head; n; --n, p = p->succ) + p->succ = new Link(val); + } + + template::value>> + slist(InputIt first, InputIt last) { + for (auto p = &head; first != last; ++first, p = p->succ) + p->succ = new Link(*first); + } + + slist(std::initializer_list lst) :slist(lst.begin(), lst.end()) {} + + slist(const slist& l) :slist(l.begin(), l.end()) {} + + slist& operator=(const slist& l) { + if (this != &l) assign(l.begin(), l.end()); + return *this; + } - slist() :first(nullptr), sz(0) {} + slist(slist&& l) noexcept :head(l.head) { l.head.succ = nullptr; } - ~slist() { - while (first) { - auto next = first->succ; - delete first; - first = next; - } + slist& operator=(slist&& l) noexcept { + clear(); + head.succ = l.head.succ; + l.head.succ = nullptr; + return *this; } + ~slist() { clear(); } + + // iterator to one before forst element + iterator before_begin() { return iterator(&head); } + const_iterator before_begin() const { return const_iterator(&head); } + // iterator to first element - iterator begin() { return iterator(first); } + 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(nullptr); } + const_iterator end() const { return const_iterator(nullptr); } - iterator insert_after(iterator p, const Elem& v); // insert v into list after p - iterator erase_after(iterator p); // remove the element after p from the list + iterator insert_after(iterator p, const T& v); + iterator insert_after(iterator p, size_type n, const T& val); - void push_front(const Elem& v); // insert v at front - void pop_front(); // remove the first element + template::value>> + iterator insert_after(iterator p, InputIt first, InputIt last); - Elem& front(); // the first element + iterator erase_after(iterator p); + iterator erase_after(iterator first, iterator last); - int size() const { return sz; }; + void clear(); -private: - Link* first; - int sz; -}; + void assign(size_type n, const T& val); -template -class slist::iterator { -public: - explicit iterator(Link* p) :curr(p) {} - iterator& operator++() { curr = curr->succ; return *this; } // forward - Elem& operator*() { return curr->val; } // get value (dereference) - bool operator==(const iterator& b) const { return curr == b.curr; } - bool operator!=(const iterator& b) const { return curr != b.curr; } + template::value>> + void assign(InputIt first, InputIt last); + + // insert v at front + void push_front(const T& v) { insert_after(before_begin(), v); } + + // remove the first element + void pop_front() { erase_after(before_begin()); } + + // the first element + T& front() { return *begin(); } + const T& front() const { return *begin(); } + + bool empty() const { return head.succ == nullptr; } + + void splice_after(iterator p, slist& l) { splice_after(p, l, l.before_begin(), l.end()); } + + // transfer elements in (first, last) from l to this list, insert after p + void splice_after(iterator p, slist& l, iterator first, iterator last) { _splice_after(p, first, last); } private: - Link* curr; // current link + iterator _splice_after(iterator p, iterator first, iterator last); + + Node_base head; // virtual head node }; -template -void slist::push_front(const Elem& v) { - if (!first) - first = new Link{nullptr, nullptr, v}; - else - first = first->prev = new Link{nullptr, first, v}; - ++sz; +// insert v into list after p +template +typename slist::iterator slist::insert_after(iterator p, const T& v) { + auto tmp = new Link(v); + p.node->insert_after(tmp); + return iterator(tmp); +} + +// insert n copies of val into list after p +template +typename slist::iterator slist::insert_after(iterator p, slist::size_type n, const T& val) { + if (n == 0) return p; + slist tmp(n, val); + return _splice_after(p, tmp.before_begin(), tmp.end()); +} + +// insert [first, last) into list after p +template +template +typename slist::iterator slist::insert_after(iterator p, InputIt first, InputIt last) { + if (first == last) return p; + slist tmp(first, last); + return _splice_after(p, tmp.before_begin(), tmp.end()); +} + +// remove element following p from the list +template +typename slist::iterator slist::erase_after(iterator p) { + auto next = p.node->succ; + p.node->erase_after(); + delete static_cast*>(next); + return iterator(p.node->succ); +} + +// remove (first, last) from the list +template +typename slist::iterator slist::erase_after(iterator first, iterator last) { + while (first.node->succ != last.node) + erase_after(first); + return last; +} + +template +void slist::clear() { + while (head.succ) { + auto p = head.succ->succ; + delete static_cast*>(head.succ); + head.succ = p; + } +} + +// replace the contents with n copies of val +template +void slist::assign(slist::size_type n, const T& val) { + iterator prev = before_begin(), curr = begin(), end_ = end(); + for (; curr != end_ && n > 0; ++prev, ++curr, --n) + *curr = val; + if (n > 0) + insert_after(prev, n, val); + else if (curr != end_) + erase_after(prev, end_); +} + +// replace the contents with [first, last) +template +template +void slist::assign(InputIt first, InputIt last) { + iterator prev = before_begin(), curr = begin(), end_ = end(); + for (; curr != end_ && first != last; ++prev, ++curr, ++first) + *curr = *first; + if (first != last) + insert_after(prev, first, last); + else if (curr != end_) + erase_after(prev, end_); +} + +template +typename slist::iterator slist::_splice_after(iterator p, iterator first, iterator last) { + Node_base* e = first.node; + while (e->succ != last.node) e = e->succ; + if (first.node == e) return iterator(p); + p.node->insert_after(first.node, e); + return iterator(e); } diff --git a/ch20/singly_linked_list_test.cpp b/ch20/singly_linked_list_test.cpp index e69de29..1c9c426 100644 --- a/ch20/singly_linked_list_test.cpp +++ b/ch20/singly_linked_list_test.cpp @@ -0,0 +1,247 @@ +#include +#include + +#include "ch19/counted_element.h" +#include "singly_linked_list.h" + +class SinglyLinkedListTest : public ::testing::Test { +protected: + slist l_ = {0.0, 1.1, 2.2, 3.3, 4.4}; +}; + +// for testing list of class without default +struct No_default { + No_default(int) {} +}; + +template +size_t size(const slist& l) { + return std::distance(l.begin(), l.end()); +} + +TEST_F(SinglyLinkedListTest, DefaultConstructor) { + slist l; + EXPECT_TRUE(l.empty()); + EXPECT_EQ(l.begin(), l.end()); +} + +TEST_F(SinglyLinkedListTest, Constructor) { + slist ld(3); + EXPECT_EQ(size(ld), 3); + for (double d : ld) + EXPECT_DOUBLE_EQ(d, 0); + + slist li(5, 8); + EXPECT_EQ(size(li), 5); + for (int i : li) + EXPECT_EQ(i, 8); + + // class without default + slist ln(10, No_default(8)); + EXPECT_EQ(size(ln), 10); + + // class with resources + slist le(10); + EXPECT_EQ(Counted_element::count(), 10); +} + +TEST_F(SinglyLinkedListTest, InitializerListConstructor) { + slist ld = {1, 2, 3}; + double expected[] = {1, 2, 3}; + EXPECT_EQ(size(ld), 3); + EXPECT_TRUE(std::equal(ld.begin(), ld.end(), expected)); + + // class with resources + slist le = {Counted_element(), Counted_element(), Counted_element()}; + EXPECT_EQ(Counted_element::count(), 3); +} + +TEST_F(SinglyLinkedListTest, CopyConstructor) { + slist l2 = l_; + EXPECT_EQ(size(l2), size(l_)); + 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 + slist le(5); + EXPECT_EQ(Counted_element::count(), 5); + slist le2 = le; + EXPECT_EQ(Counted_element::count(), 10); +} + +TEST_F(SinglyLinkedListTest, CopyAssignment) { + // self-assignment + l_ = l_; + double expected[] = {0.0, 1.1, 2.2, 3.3, 4.4}; + EXPECT_EQ(size(l_), 5); + EXPECT_TRUE(std::equal(l_.begin(), l_.end(), expected)); + + slist l2 = {1, 2, 3}; + l2 = l_; + EXPECT_EQ(size(l2), size(l_)); + 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 + slist le(5), le2(8); + EXPECT_EQ(Counted_element::count(), 13); + le2 = le; + EXPECT_EQ(Counted_element::count(), 10); +} + +TEST_F(SinglyLinkedListTest, MoveConstructor) { + int sz = size(l_); + slist l2 = std::move(l_); + double expected[] = {0.0, 1.1, 2.2, 3.3, 4.4}; + EXPECT_EQ(size(l2), sz); + EXPECT_TRUE(std::equal(l2.begin(), l2.end(), expected)); + EXPECT_TRUE(l_.empty()); + + // class with resources + slist le(5); + EXPECT_EQ(Counted_element::count(), 5); + slist le2 = std::move(le); + EXPECT_EQ(Counted_element::count(), 5); +} + +TEST_F(SinglyLinkedListTest, MoveAssignment) { + slist l2 = {1, 2, 3}; + int sz = size(l_); + l2 = std::move(l_); + double expected[] = {0.0, 1.1, 2.2, 3.3, 4.4}; + EXPECT_EQ(size(l2), sz); + EXPECT_TRUE(std::equal(l2.begin(), l2.end(), expected)); + EXPECT_TRUE(l_.empty()); + + // class with resources + slist le(5), le2(8); + EXPECT_EQ(Counted_element::count(), 13); + le2 = std::move(le); + EXPECT_EQ(Counted_element::count(), 5); +} + +TEST_F(SinglyLinkedListTest, Destructor) { + // class with resources + auto ple = new slist(8); + EXPECT_EQ(Counted_element::count(), 8); + delete ple; + EXPECT_EQ(Counted_element::count(), 0); +} + +TEST_F(SinglyLinkedListTest, Iterator) { + auto it = std::find(l_.begin(), l_.end(), 3.3); + EXPECT_NE(it, l_.end()); + EXPECT_DOUBLE_EQ(*it, 3.3); + EXPECT_EQ(std::find(l_.begin(), l_.end(), 9.9), l_.end()); + + double a[5]; + std::copy(l_.begin(), l_.end(), a); + EXPECT_TRUE(std::equal(l_.begin(), l_.end(), a)); +} + +TEST_F(SinglyLinkedListTest, Insert) { + // empty list + slist l0; + auto p = l0.insert_after(l0.before_begin(), 42); + EXPECT_EQ(p, l0.begin()); + EXPECT_DOUBLE_EQ(*p, 42); + EXPECT_EQ(size(l0), 1); + + // insert at middle + p = l_.insert_after(std::next(l_.begin(), 3), 42); + EXPECT_EQ(std::distance(l_.begin(), p), 4); + EXPECT_DOUBLE_EQ(*p, 42); + EXPECT_EQ(size(l_), 6); + + // can't insert at end() + + // insert at begin() + p = l_.insert_after(l_.before_begin(), -1.1); + EXPECT_EQ(p, l_.begin()); + EXPECT_DOUBLE_EQ(*p, -1.1); + EXPECT_EQ(size(l_), 7); + + // class with resources + slist le(5); + EXPECT_EQ(Counted_element::count(), 5); + le.insert_after(std::next(le.begin(), 2), Counted_element()); + EXPECT_EQ(Counted_element::count(), 6); +} + +TEST_F(SinglyLinkedListTest, Erase) { + // erase last element + auto p = l_.erase_after(std::next(l_.begin(), 3)); + EXPECT_EQ(p, l_.end()); + EXPECT_EQ(size(l_), 4); + + // erase first element + p = l_.erase_after(l_.before_begin()); + EXPECT_EQ(p, l_.begin()); + EXPECT_DOUBLE_EQ(*p, 1.1); + EXPECT_EQ(size(l_), 3); + + // erase middle element + p = l_.erase_after(l_.begin()); + EXPECT_EQ(p, std::next(l_.begin())); + EXPECT_DOUBLE_EQ(*p, 3.3); + EXPECT_EQ(size(l_), 2); + + // class with resources + slist le(5); + EXPECT_EQ(Counted_element::count(), 5); + le.erase_after(std::next(le.begin(), 2)); + EXPECT_EQ(Counted_element::count(), 4); +} + +TEST_F(SinglyLinkedListTest, Clear) { + // empty list + slist l0; + l0.clear(); + EXPECT_TRUE(l0.empty()); + + // non-empty list + l_.clear(); + EXPECT_TRUE(l_.empty()); + + // class with resources + slist le(5); + EXPECT_EQ(Counted_element::count(), 5); + le.clear(); + EXPECT_EQ(Counted_element::count(), 0); +} + +TEST_F(SinglyLinkedListTest, PushFront) { + // empty list + slist l0; + l0.push_front(42); + EXPECT_EQ(size(l0), 1); + EXPECT_DOUBLE_EQ(l0.front(), 42); + + // non-empty list + l_.push_front(5.5); + EXPECT_EQ(size(l_), 6); + EXPECT_DOUBLE_EQ(l_.front(), 5.5); + + // class with resources + slist le(5); + EXPECT_EQ(Counted_element::count(), 5); + le.push_front(Counted_element()); + EXPECT_EQ(Counted_element::count(), 6); +} + +TEST_F(SinglyLinkedListTest, PopFront) { + l_.pop_front(); + EXPECT_EQ(size(l_), 4); + + // class with resources + slist le(5); + EXPECT_EQ(Counted_element::count(), 5); + le.pop_front(); + EXPECT_EQ(Counted_element::count(), 4); +}