Ginsutime

Cherno Vector Header tutorial

Feb 21st, 2022
783
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 3.36 KB | None | 0 0
  1. #pragma once
  2.  
  3. template<typename T>
  4. class Vector
  5. {
  6. public:
  7.     Vector()
  8.     {
  9.         // allocate memory for us to store 2 elements
  10.         ReAlloc(2);
  11.     }
  12.  
  13.     ~Vector() // Won't go and call the destructors, Clear does so
  14.     {
  15.         Clear();
  16.         ::operator delete(m_Data, m_Capacity * sizeof(T));
  17.     }
  18.  
  19.     void PushBack(const T& value)
  20.     {
  21.         // If we attempt to push back an element and there's no room, ReAlloc inside is called
  22.         if (m_Size >= m_Capacity)
  23.         {
  24.             ReAlloc(m_Capacity + m_Capacity / 2); // Growing by 1.5x (50%) each time
  25.         }
  26.  
  27.         // Basic way of moving things into array
  28.         m_Data[m_Size] = value;
  29.         m_Size++;
  30.     }
  31.  
  32.     void PushBack(T&& value) // Temporary value (rvalue), does moving instead of copying like one above
  33.     {
  34.         if (m_Size >= m_Capacity)
  35.         {
  36.             ReAlloc(m_Capacity + m_Capacity / 2);
  37.         }
  38.  
  39.         // 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
  40.         m_Data[m_Size] = std::move(value);
  41.         m_Size++;
  42.     }
  43.  
  44.     template<typename... Args>
  45.     T& EmplaceBack(Args&&... args)
  46.     {
  47.         if (m_Size >= m_Capacity)
  48.             ReAlloc(m_Capacity + m_Capacity / 2);
  49.  
  50.         // Placement New for constructing objects in place
  51.         new(&m_Data[m_Size]) T(std::forward<Args>(args)...); // Forward all arguments to constructor, triple dot to unpack the arguments
  52.         return m_Data[m_Size++];
  53.     }
  54.  
  55.     void PopBack()
  56.     {
  57.         if (m_Size > 0)
  58.         {
  59.             m_Size--;
  60.             m_Data[m_Size].~T();
  61.         }
  62.     }
  63.  
  64.     void Clear() // Will go through and call all the destructors
  65.     {
  66.         for (size_t i = 0; i < m_Size; i++)
  67.             m_Data[i].~T();
  68.  
  69.         m_Size = 0;
  70.     }
  71.  
  72.     const T& operator[](size_t index) const
  73.     {
  74.         if (index > m_Size) // If trying to access index outside bounds of array
  75.         {
  76.             // assert
  77.         }
  78.         return m_Data[index];
  79.     }
  80.  
  81.     T& operator[](size_t index)
  82.     {
  83.         if (index > m_Size) // If trying to access index outside bounds of array
  84.         {
  85.             // assert
  86.         }
  87.         return m_Data[index];
  88.     }
  89.  
  90.     size_t Size() const { return m_Size; } // Function that returns our size
  91. private:
  92.     void ReAlloc(size_t newCapacity)
  93.     {
  94.         // Steps in order
  95.         // 1. Needs to allocate a new block of memory
  96.         // 2. Copy/move old elements into new block, preferably move
  97.         // 3. Delete old block
  98.         // Want to use versions of new and delete that don't call the constructor or destructor
  99.  
  100.         T* newBlock = (T*)::operator new(newCapacity * sizeof(T)); // Raw pointers preferred when doing something low level like this
  101.  
  102.         // If downsizing instead of growing array, ensures array isn't overflowed -> done by only copying up to newCapacity size of elements
  103.         if (newCapacity < m_Size)
  104.             m_Size = newCapacity;
  105.  
  106.         for (size_t i = 0; i < m_Size; i++)
  107.             newBlock[i] = std::move(m_Data[i]); // For more complex types like classes (vs primitives likes ints), can't use memcpy
  108.  
  109.         for (size_t i = 0; i < m_Size; i++)
  110.             m_Data[i].~T();
  111.  
  112.         ::operator delete(m_Data, m_Capacity * sizeof(T)); // Deletes old block
  113.         m_Data = newBlock; // Assign data to new block
  114.         m_Capacity = newCapacity; // Make sure our capacity matches new capacity
  115.     }
  116. private:
  117.     T* m_Data = nullptr; // Pointer to whatever our type is, nullptr as default so code is easier to debug
  118.  
  119.     // Need those so you're not reallocating memory as often
  120.     size_t m_Size = 0; // Number of elements in our vector
  121.     size_t m_Capacity = 0; // How much memory have we allocated, how much could we store without having to reallocate
  122. };
Advertisement
Add Comment
Please, Sign In to add comment