Advertisement
Ginsutime

Cherno Iterator Vector Setup Vector Header

Feb 23rd, 2022
1,199
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 6.45 KB | None | 0 0
  1. #pragma once
  2.  
  3. template<typename Vector>
  4. class VectorIterator
  5. {
  6. public:
  7.     using ValueType = typename Vector::ValueType;
  8.     using PointerType = ValueType*;
  9.     using ReferenceType = ValueType&;
  10. public:
  11.     VectorIterator(PointerType ptr)
  12.         : m_Ptr(ptr) {}
  13.  
  14.     VectorIterator& operator++() // Prefix operator
  15.     {
  16.         m_Ptr++; // increments actual pointer by correct amount of bytes because it increments by the size of the type
  17.         return *this; // vector iterator ref
  18.     }
  19.  
  20.     VectorIterator operator++(int) // Postfix operator differentiated by taking in an int vs nothing for other
  21.     {
  22.         VectorIterator iterator = *this; // Returns copy of iterator since we're not supposed to modify obj in place
  23.         ++(*this); // Calling prefix operator here
  24.         return iterator;
  25.     }
  26.  
  27.     VectorIterator& operator--() // Prefix operator
  28.     {
  29.         m_Ptr--; // increments actual pointer by correct amount of bytes because it increments by the size of the type
  30.         return *this; // vector iterator ref
  31.     }
  32.  
  33.     VectorIterator operator--(int) // Postfix operator differentiated by taking in an int vs nothing for other
  34.     {
  35.         VectorIterator iterator = *this; // Returns copy of iterator since we're not supposed to modify obj in place
  36.         --(*this); // Calling prefix operator here
  37.         return iterator;
  38.     }
  39.  
  40.     ReferenceType operator[](int index)
  41.     {
  42.         return *(m_Ptr + index);
  43.     }
  44.  
  45.     PointerType operator->()
  46.     {
  47.         return m_Ptr; // current position of the iterator
  48.     }
  49.  
  50.     ReferenceType operator*() // dereference operator
  51.     {
  52.         return *m_Ptr; // return dereferenced pointer
  53.     }
  54.  
  55.     bool operator==(const VectorIterator& other) const
  56.     {
  57.         return m_Ptr == other.m_Ptr;
  58.     }
  59.  
  60.     bool operator!=(const VectorIterator& other) const
  61.     {
  62.         return !(*this == other);
  63.     }
  64. private:
  65.     PointerType m_Ptr; // Don't have to assign value since doing this in the constructor
  66. };
  67.  
  68. template<typename T>
  69. class Vector
  70. {
  71. public:
  72.     using ValueType = T;
  73.     using Iterator = VectorIterator<Vector<T>>;
  74. public:
  75.     Vector()
  76.     {
  77.         // allocate memory for us to store 2 elements
  78.         ReAlloc(2);
  79.     }
  80.  
  81.     ~Vector() // Won't go and call the destructors, Clear does so
  82.     {
  83.         Clear();
  84.         ::operator delete(m_Data, m_Capacity * sizeof(T));
  85.     }
  86.  
  87.     void PushBack(const T& value)
  88.     {
  89.         // If we attempt to push back an element and there's no room, ReAlloc inside is called
  90.         if (m_Size >= m_Capacity)
  91.         {
  92.             ReAlloc(m_Capacity + m_Capacity / 2); // Growing by 1.5x (50%) each time
  93.         }
  94.  
  95.         // Basic way of moving things into array
  96.         m_Data[m_Size] = value;
  97.         m_Size++;
  98.     }
  99.  
  100.     void PushBack(T&& value) // Temporary value (rvalue), does moving instead of copying like one above
  101.     {
  102.         if (m_Size >= m_Capacity)
  103.         {
  104.             ReAlloc(m_Capacity + m_Capacity / 2);
  105.         }
  106.  
  107.         // Once enter a function like this, RValue ends up becoming an LValue, so we use std move to still treat it like an RValue when moving
  108.         m_Data[m_Size] = std::move(value);
  109.         m_Size++;
  110.     }
  111.  
  112.     template<typename... Args>
  113.     T& EmplaceBack(Args&&... args)
  114.     {
  115.         if (m_Size >= m_Capacity)
  116.             ReAlloc(m_Capacity + m_Capacity / 2);
  117.  
  118.         // Placement New for constructing objects in place
  119.         new(&m_Data[m_Size]) T(std::forward<Args>(args)...); // Forward all arguments to constructor, triple dot to unpack the arguments
  120.         return m_Data[m_Size++];
  121.     }
  122.  
  123.     void PopBack()
  124.     {
  125.         if (m_Size > 0)
  126.         {
  127.             m_Size--;
  128.             m_Data[m_Size].~T();
  129.         }
  130.     }
  131.  
  132.     void Clear() // Will go through and call all the destructors
  133.     {
  134.         for (size_t i = 0; i < m_Size; i++)
  135.             m_Data[i].~T();
  136.  
  137.         m_Size = 0;
  138.     }
  139.  
  140.     const T& operator[](size_t index) const
  141.     {
  142.         if (index > m_Size) // If trying to access index outside bounds of array
  143.         {
  144.             // assert
  145.         }
  146.         return m_Data[index];
  147.     }
  148.  
  149.     T& operator[](size_t index)
  150.     {
  151.         if (index > m_Size) // If trying to access index outside bounds of array
  152.         {
  153.             // assert
  154.         }
  155.         return m_Data[index];
  156.     }
  157.  
  158.     size_t Size() const { return m_Size; } // Function that returns our size
  159.  
  160.     Iterator begin()
  161.     {
  162.         return Iterator(m_Data); // beginning of data block is m_Data
  163.     }
  164.  
  165.     Iterator end()
  166.     {
  167.         return Iterator(m_Data + m_Size); // Sends it to first byte of memory outside m_Data allocation
  168.     }
  169. private:
  170.     void ReAlloc(size_t newCapacity)
  171.     {
  172.         // Steps in order
  173.         // 1. Needs to allocate a new block of memory
  174.         // 2. Copy/move old elements into new block, preferably move
  175.         // 3. Delete old block
  176.         // Want to use versions of new and delete that don't call the constructor or destructor
  177.  
  178.         T* newBlock = (T*)::operator new(newCapacity * sizeof(T)); // Raw pointers preferred when doing something low level like this
  179.  
  180.         // If downsizing instead of growing array, ensures array isn't overflowed -> done by only copying up to newCapacity size of elements
  181.         if (newCapacity < m_Size)
  182.             m_Size = newCapacity;
  183.  
  184.         for (size_t i = 0; i < m_Size; i++)
  185.             new (&newBlock[i]) T(std::move(m_Data[i]));
  186.             // Line above calls constructor properly so that current newBlock string that has tidy_dellocate() called on it actually exists
  187.             // If constructor isn't called properly, string won't exist and we'll crash.
  188.             // Can't use newBlock[i] = std::move(m_Data[i]); since assignment operator (=) requires newBlock[i] to exist
  189.             // Use of placement new fixes this
  190.  
  191.         for (size_t i = 0; i < m_Size; i++)
  192.             m_Data[i].~T();
  193.  
  194.         ::operator delete(m_Data, m_Capacity * sizeof(T)); // Deletes old block
  195.         m_Data = newBlock; // Assign data to new block
  196.         m_Capacity = newCapacity; // Make sure our capacity matches new capacity
  197.     }
  198. private:
  199.     T* m_Data = nullptr; // Pointer to whatever our type is, nullptr as default so code is easier to debug
  200.  
  201.     // Need those so you're not reallocating memory as often
  202.     size_t m_Size = 0; // Number of elements in our vector
  203.     size_t m_Capacity = 0; // How much memory have we allocated, how much could we store without having to reallocate
  204. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement