From 728819033f7abfc03445534161cd5a15a3cc17b6 Mon Sep 17 00:00:00 2001 From: ZhuangYumin Date: Sun, 25 Feb 2024 15:35:43 +0000 Subject: [PATCH] initial version --- .gitignore | 6 + CMakeLists.txt | 5 + vector/CMakeLists.txt | 4 + vector/src/vector.hpp | 665 +++++++++++++++++++++++++++++------------- 4 files changed, 471 insertions(+), 209 deletions(-) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 vector/CMakeLists.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b569ed9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/.vscode +/.devcontainer +/.github +/build +/.cache +.clang-format \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f45e64a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.10) +Project(STLite-ACM-2024) +include(CTest) +add_subdirectory(vector) +enable_testing() \ No newline at end of file diff --git a/vector/CMakeLists.txt b/vector/CMakeLists.txt new file mode 100644 index 0000000..3e1e4c4 --- /dev/null +++ b/vector/CMakeLists.txt @@ -0,0 +1,4 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) +add_executable(one ${CMAKE_CURRENT_SOURCE_DIR}/data/one/code.cpp) +add_test(NAME vector_one COMMAND one >/tmp/one_out.txt + && diff -u ${CMAKE_CURRENT_SOURCE_DIR}/data/one/answer.txt /tmp/one_out.txt>/tmp/one_diff.txt) \ No newline at end of file diff --git a/vector/src/vector.hpp b/vector/src/vector.hpp index 6206c74..6b30786 100644 --- a/vector/src/vector.hpp +++ b/vector/src/vector.hpp @@ -5,222 +5,469 @@ #include #include - -namespace sjtu -{ +#include +#include +#include +#include +namespace sjtu { /** * a data container like std::vector * store data in a successive memory and support random access. */ -template -class vector -{ -public: - /** - * TODO - * a type for actions of the elements of a vector, and you should write - * a class named const_iterator with same interfaces. - */ - /** - * you can see RandomAccessIterator at CppReference for help. - */ - class const_iterator; - class iterator - { - // The following code is written for the C++ type_traits library. - // Type traits is a C++ feature for describing certain properties of a type. - // For instance, for an iterator, iterator::value_type is the type that the - // iterator points to. - // STL algorithms and containers may use these type_traits (e.g. the following - // typedef) to work properly. In particular, without the following code, - // @code{std::sort(iter, iter1);} would not compile. - // See these websites for more information: - // https://en.cppreference.com/w/cpp/header/type_traits - // About value_type: https://blog.csdn.net/u014299153/article/details/72419713 - // About iterator_category: https://en.cppreference.com/w/cpp/iterator - public: - using difference_type = std::ptrdiff_t; - using value_type = T; - using pointer = T*; - using reference = T&; - using iterator_category = std::output_iterator_tag; +template +class vector { + std::allocator alloc; + size_t allocated_length; + size_t current_length; + T *raw_beg, *raw_end; - private: - /** - * TODO add data members - * just add whatever you want. - */ - public: - /** - * return a new iterator which pointer n-next elements - * as well as operator- - */ - iterator operator+(const int &n) const - { - //TODO - } - iterator operator-(const int &n) const - { - //TODO - } - // return the distance between two iterators, - // if these two iterators point to different vectors, throw invaild_iterator. - int operator-(const iterator &rhs) const - { - //TODO - } - iterator& operator+=(const int &n) - { - //TODO - } - iterator& operator-=(const int &n) - { - //TODO - } - /** - * TODO iter++ - */ - iterator operator++(int) {} - /** - * TODO ++iter - */ - iterator& operator++() {} - /** - * TODO iter-- - */ - iterator operator--(int) {} - /** - * TODO --iter - */ - iterator& operator--() {} - /** - * TODO *it - */ - T& operator*() const{} - /** - * a operator to check whether two iterators are same (pointing to the same memory address). - */ - bool operator==(const iterator &rhs) const {} - bool operator==(const const_iterator &rhs) const {} - /** - * some other operator for iterator. - */ - bool operator!=(const iterator &rhs) const {} - bool operator!=(const const_iterator &rhs) const {} - }; - /** - * TODO - * has same function as iterator, just for a const object. - */ - class const_iterator - { - public: - using difference_type = std::ptrdiff_t; - using value_type = T; - using pointer = T*; - using reference = T&; - using iterator_category = std::output_iterator_tag; - - private: - /*TODO*/ + public: + /** + * you can see RandomAccessIterator at CppReference for help. + */ + class const_iterator; + class iterator { + // The following code is written for the C++ type_traits library. + // Type traits is a C++ feature for describing certain properties of a type. + // For instance, for an iterator, iterator::value_type is the type that the + // iterator points to. + // STL algorithms and containers may use these type_traits (e.g. the following + // typedef) to work properly. In particular, without the following code, + // @code{std::sort(iter, iter1);} would not compile. + // See these websites for more information: + // https://en.cppreference.com/w/cpp/header/type_traits + // About value_type: https://blog.csdn.net/u014299153/article/details/72419713 + // About iterator_category: https://en.cppreference.com/w/cpp/iterator + friend class vector; - }; - /** - * TODO Constructs - * At least two: default constructor, copy constructor - */ - vector() {} - vector(const vector &other) {} - /** - * TODO Destructor - */ - ~vector() {} - /** - * TODO Assignment operator - */ - vector &operator=(const vector &other) {} - /** - * assigns specified element with bounds checking - * throw index_out_of_bound if pos is not in [0, size) - */ - T & at(const size_t &pos) {} - const T & at(const size_t &pos) const {} - /** - * assigns specified element with bounds checking - * throw index_out_of_bound if pos is not in [0, size) - * !!! Pay attentions - * In STL this operator does not check the boundary but I want you to do. - */ - T & operator[](const size_t &pos) {} - const T & operator[](const size_t &pos) const {} - /** - * access the first element. - * throw container_is_empty if size == 0 - */ - const T & front() const {} - /** - * access the last element. - * throw container_is_empty if size == 0 - */ - const T & back() const {} - /** - * returns an iterator to the beginning. - */ - iterator begin() {} - const_iterator cbegin() const {} - /** - * returns an iterator to the end. - */ - iterator end() {} - const_iterator cend() const {} - /** - * checks whether the container is empty - */ - bool empty() const {} - /** - * returns the number of elements - */ - size_t size() const {} - /** - * clears the contents - */ - void clear() {} - /** - * inserts value before pos - * returns an iterator pointing to the inserted value. - */ - iterator insert(iterator pos, const T &value) {} - /** - * inserts value at index ind. - * after inserting, this->at(ind) == value - * returns an iterator pointing to the inserted value. - * throw index_out_of_bound if ind > size (in this situation ind can be size because after inserting the size will increase 1.) - */ - iterator insert(const size_t &ind, const T &value) {} - /** - * removes the element at pos. - * return an iterator pointing to the following element. - * If the iterator pos refers the last element, the end() iterator is returned. - */ - iterator erase(iterator pos) {} - /** - * removes the element with index ind. - * return an iterator pointing to the following element. - * throw index_out_of_bound if ind >= size - */ - iterator erase(const size_t &ind) {} - /** - * adds an element to the end. - */ - void push_back(const T &value) {} - /** - * remove the last element from the end. - * throw container_is_empty if size() == 0 - */ - void pop_back() {} + public: + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = T *; + using reference = T &; + using iterator_category = std::random_access_iterator_tag; + + private: + vector *domain; + T *raw_pointer; + iterator(vector *domain, T *raw_pointer) : domain(domain), raw_pointer(raw_pointer) {} + + public: + /** + * return a new iterator which pointer n-next elements + * as well as operator- + */ + iterator operator+(const int &n) const { + iterator temp = *this; + temp.raw_pointer += n; + return temp; + } + iterator operator-(const int &n) const { + iterator temp = *this; + temp.raw_pointer -= n; + return temp; + } + // return the distance between two iterators, + // if these two iterators point to different vectors, throw invaild_iterator. + int operator-(const iterator &rhs) const { + if (domain != rhs.domain) throw invalid_iterator(); + return raw_pointer - rhs.raw_pointer; + } + iterator &operator+=(const int &n) { + raw_pointer += n; + return *this; + } + iterator &operator-=(const int &n) { + raw_pointer -= n; + return *this; + } + /** + * TODO iter++ + */ + iterator operator++(int) { + iterator temp = *this; + raw_pointer++; + return temp; + } + /** + * TODO ++iter + */ + iterator &operator++() { + raw_pointer++; + return *this; + } + /** + * TODO iter-- + */ + iterator operator--(int) { + iterator temp = *this; + raw_pointer--; + return temp; + } + /** + * TODO --iter + */ + iterator &operator--() { + raw_pointer--; + return *this; + } + /** + * TODO *it + */ + T &operator*() const { return *raw_pointer; } + /** + * a operator to check whether two iterators are same (pointing to the same memory address). + */ + bool operator==(const iterator &rhs) const { return raw_pointer == rhs.raw_pointer; } + bool operator==(const const_iterator &rhs) const { return raw_pointer == rhs.raw_pointer; } + /** + * some other operator for iterator. + */ + bool operator!=(const iterator &rhs) const { return raw_pointer != rhs.raw_pointer; } + bool operator!=(const const_iterator &rhs) const { return raw_pointer != rhs.raw_pointer; } + }; + /** + * TODO + * has same function as iterator, just for a const object. + */ + class const_iterator { + friend class vector; + + public: + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = T *; + using reference = T &; + using iterator_category = std::random_access_iterator_tag; + + private: + const vector *domain; + const T *raw_pointer; + const_iterator(const vector *domain, const T *raw_pointer) : domain(domain), raw_pointer(raw_pointer) {} + + public: + /** + * return a new iterator which pointer n-next elements + * as well as operator- + */ + const_iterator operator+(const int &n) const { + const_iterator temp = *this; + temp.raw_pointer += n; + return temp; + } + const_iterator operator-(const int &n) const { + const_iterator temp = *this; + temp.raw_pointer -= n; + return temp; + } + // return the distance between two iterators, + // if these two iterators point to different vectors, throw invaild_iterator. + int operator-(const const_iterator &rhs) const { + if (domain != rhs.domain) throw invalid_iterator(); + return raw_pointer - rhs.raw_pointer; + } + const_iterator &operator+=(const int &n) { + raw_pointer += n; + return *this; + } + const_iterator &operator-=(const int &n) { + raw_pointer -= n; + return *this; + } + /** + * TODO iter++ + */ + const_iterator operator++(int) { + const_iterator temp = *this; + raw_pointer++; + return temp; + } + /** + * TODO ++iter + */ + const_iterator &operator++() { + raw_pointer++; + return *this; + } + /** + * TODO iter-- + */ + const_iterator operator--(int) { + const_iterator temp = *this; + raw_pointer--; + return temp; + } + /** + * TODO --iter + */ + const_iterator &operator--() { + raw_pointer--; + return *this; + } + /** + * TODO *it + */ + const T &operator*() const { return *raw_pointer; } + /** + * a operator to check whether two iterators are same (pointing to the same memory address). + */ + bool operator==(const iterator &rhs) const { return raw_pointer == rhs.raw_pointer; } + bool operator==(const const_iterator &rhs) const { return raw_pointer == rhs.raw_pointer; } + /** + * some other operator for iterator. + */ + bool operator!=(const iterator &rhs) const { return raw_pointer != rhs.raw_pointer; } + bool operator!=(const const_iterator &rhs) const { return raw_pointer != rhs.raw_pointer; } + }; + /** + * TODO Constructs + * At least two: default constructor, copy constructor + */ + vector() { + raw_beg = alloc.allocate(1); + raw_end = raw_beg; + allocated_length = 1; + current_length = 0; + } + vector(const vector &other) { + raw_beg = alloc.allocate(other.allocated_length); + raw_end = raw_beg + other.current_length; + allocated_length = other.allocated_length; + current_length = other.current_length; + for (size_t i = 0; i < current_length; ++i) { + alloc.construct(raw_beg + i, other.raw_beg[i]); + } + } + vector(vector &&other) noexcept { + raw_beg = other.raw_beg; + raw_end = other.raw_end; + allocated_length = other.allocated_length; + current_length = other.current_length; + other.raw_beg = nullptr; + other.raw_end = nullptr; + other.allocated_length = 0; + other.current_length = 0; + } + ~vector() { + if (raw_beg != nullptr) { + for (size_t i = 0; i < current_length; ++i) { + alloc.destroy(raw_beg + i); + } + alloc.deallocate(raw_beg, allocated_length); + } + } + /** + * TODO Assignment operator + */ + vector &operator=(const vector &other) { + if (this == &other) return *this; + if (raw_beg != nullptr) { + for (size_t i = 0; i < current_length; ++i) { + alloc.destroy(raw_beg + i); + } + alloc.deallocate(raw_beg, allocated_length); + } + raw_beg = alloc.allocate(other.allocated_length); + raw_end = raw_beg + other.current_length; + allocated_length = other.allocated_length; + current_length = other.current_length; + for (size_t i = 0; i < current_length; ++i) { + alloc.construct(raw_beg + i, other.raw_beg[i]); + } + return *this; + } + /** + * assigns specified element with bounds checking + * throw index_out_of_bound if pos is not in [0, size) + */ + T &at(const size_t &pos) { + if (pos < 0 || pos >= current_length) throw index_out_of_bound(); + return raw_beg[pos]; + } + const T &at(const size_t &pos) const { + if (pos < 0 || pos >= current_length) throw index_out_of_bound(); + return raw_beg[pos]; + } + /** + * assigns specified element with bounds checking + * throw index_out_of_bound if pos is not in [0, size) + * !!! Pay attentions + * In STL this operator does not check the boundary but I want you to do. + */ + T &operator[](const size_t &pos) { + if (pos < 0 || pos >= current_length) throw index_out_of_bound(); + return raw_beg[pos]; + } + const T &operator[](const size_t &pos) const { + if (pos < 0 || pos >= current_length) throw index_out_of_bound(); + return raw_beg[pos]; + } + /** + * access the first element. + * throw container_is_empty if size == 0 + */ + const T &front() const { + if (current_length == 0) throw container_is_empty(); + return raw_beg[0]; + } + /** + * access the last element. + * throw container_is_empty if size == 0 + */ + const T &back() const { + if (current_length == 0) throw container_is_empty(); + return raw_end[-1]; + } + /** + * returns an iterator to the beginning. + */ + iterator begin() { return iterator(this, raw_beg); } + const_iterator cbegin() const { return const_iterator(this, raw_beg); } + /** + * returns an iterator to the end. + */ + iterator end() { return iterator(this, raw_end); } + const_iterator cend() const { return const_iterator(this, raw_end); } + /** + * checks whether the container is empty + */ + bool empty() const { return current_length == 0; } + /** + * returns the number of elements + */ + size_t size() const { return current_length; } + /** + * clears the contents + */ + void clear() { + for (size_t i = 0; i < current_length; ++i) { + alloc.destroy(raw_beg + i); + } + current_length = 0; + alloc.deallocate(raw_beg, allocated_length); + raw_beg = alloc.allocate(1); + raw_end = raw_beg; + allocated_length = 1; + } + /** + * inserts value before pos + * returns an iterator pointing to the inserted value. + */ + iterator insert(iterator pos, const T &value) { + if (pos.raw_pointer < raw_beg || pos.raw_pointer > raw_end) throw invalid_iterator(); + if (current_length == allocated_length) { + size_t new_allocated_length = allocated_length * 2; + T *new_raw_beg = alloc.allocate(new_allocated_length); + for (size_t i = 0; i < current_length; ++i) { + alloc.construct(new_raw_beg + i, std::move(raw_beg[i])); + alloc.destroy(raw_beg + i); + } + alloc.deallocate(raw_beg, allocated_length); + raw_beg = new_raw_beg; + raw_end = raw_beg + current_length; + allocated_length = new_allocated_length; + } + for (T *i = raw_end; i != pos.raw_pointer; --i) { + alloc.construct(i, std::move(*(i - 1))); + alloc.destroy(i - 1); + } + alloc.construct(pos.raw_pointer, value); + raw_end++; + current_length++; + return pos; + } + /** + * inserts value at index ind. + * after inserting, this->at(ind) == value + * returns an iterator pointing to the inserted value. + * throw index_out_of_bound if ind > size (in this situation ind can be size because after inserting the size will + * increase 1.) + */ + iterator insert(const size_t &ind, const T &value) { + if (ind < 0 || ind > current_length) throw index_out_of_bound(); + if (current_length == allocated_length) { + size_t new_allocated_length = allocated_length * 2; + T *new_raw_beg = alloc.allocate(new_allocated_length); + for (size_t i = 0; i < current_length; ++i) { + alloc.construct(new_raw_beg + i, std::move(raw_beg[i])); + alloc.destroy(raw_beg + i); + } + alloc.deallocate(raw_beg, allocated_length); + raw_beg = new_raw_beg; + raw_end = raw_beg + current_length; + allocated_length = new_allocated_length; + } + for (T *i = raw_end; i != raw_beg + ind; --i) { + alloc.construct(i, std::move(*(i - 1))); + alloc.destroy(i - 1); + } + alloc.construct(raw_beg + ind, value); + raw_end++; + current_length++; + return iterator(this, raw_beg + ind); + } + /** + * removes the element at pos. + * return an iterator pointing to the following element. + * If the iterator pos refers the last element, the end() iterator is returned. + */ + iterator erase(iterator pos) { + if (pos.raw_pointer < raw_beg || pos.raw_pointer >= raw_end) throw invalid_iterator(); + for (T *i = pos.raw_pointer; i != raw_end - 1; ++i) { + alloc.construct(i, std::move(*(i + 1))); + alloc.destroy(i + 1); + } + raw_end--; + current_length--; + return pos; + } + /** + * removes the element with index ind. + * return an iterator pointing to the following element. + * throw index_out_of_bound if ind >= size + */ + iterator erase(const size_t &ind) { + if (ind < 0 || ind >= current_length) throw index_out_of_bound(); + for (T *i = raw_beg + ind; i != raw_end - 1; ++i) { + alloc.construct(i, std::move(*(i + 1))); + alloc.destroy(i + 1); + } + raw_end--; + current_length--; + return iterator(this, raw_beg + ind); + } + /** + * adds an element to the end. + */ + void push_back(const T &value) { + if (current_length == allocated_length) { + size_t new_allocated_length = allocated_length * 2; + T *new_raw_beg = alloc.allocate(new_allocated_length); + for (size_t i = 0; i < current_length; ++i) { + alloc.construct(new_raw_beg + i, std::move(raw_beg[i])); + alloc.destroy(raw_beg + i); + } + alloc.deallocate(raw_beg, allocated_length); + raw_beg = new_raw_beg; + raw_end = raw_beg + current_length; + allocated_length = new_allocated_length; + } + alloc.construct(raw_end, value); + raw_end++; + current_length++; + } + /** + * remove the last element from the end. + * throw container_is_empty if size() == 0 + */ + void pop_back() { + if (current_length == 0) throw container_is_empty(); + alloc.destroy(raw_end - 1); + raw_end--; + current_length--; + } }; - -} +} // namespace sjtu #endif