Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #pragma once
- template<typename T>
- class Vector
- {
- public:
- Vector()
- {
- // allocate memory for us to store 2 elements
- ReAlloc(2);
- }
- ~Vector() // Won't go and call the destructors, Clear does so
- {
- Clear();
- ::operator delete(m_Data, m_Capacity * sizeof(T));
- }
- void PushBack(const T& value)
- {
- // If we attempt to push back an element and there's no room, ReAlloc inside is called
- if (m_Size >= m_Capacity)
- {
- ReAlloc(m_Capacity + m_Capacity / 2); // Growing by 1.5x (50%) each time
- }
- // Basic way of moving things into array
- m_Data[m_Size] = value;
- m_Size++;
- }
- void PushBack(T&& value) // Temporary value (rvalue), does moving instead of copying like one above
- {
- if (m_Size >= m_Capacity)
- {
- ReAlloc(m_Capacity + m_Capacity / 2);
- }
- // 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
- m_Data[m_Size] = std::move(value);
- m_Size++;
- }
- template<typename... Args>
- T& EmplaceBack(Args&&... args)
- {
- if (m_Size >= m_Capacity)
- ReAlloc(m_Capacity + m_Capacity / 2);
- // Placement New for constructing objects in place
- new(&m_Data[m_Size]) T(std::forward<Args>(args)...); // Forward all arguments to constructor, triple dot to unpack the arguments
- return m_Data[m_Size++];
- }
- void PopBack()
- {
- if (m_Size > 0)
- {
- m_Size--;
- m_Data[m_Size].~T();
- }
- }
- void Clear() // Will go through and call all the destructors
- {
- for (size_t i = 0; i < m_Size; i++)
- m_Data[i].~T();
- m_Size = 0;
- }
- const T& operator[](size_t index) const
- {
- if (index > m_Size) // If trying to access index outside bounds of array
- {
- // assert
- }
- return m_Data[index];
- }
- T& operator[](size_t index)
- {
- if (index > m_Size) // If trying to access index outside bounds of array
- {
- // assert
- }
- return m_Data[index];
- }
- size_t Size() const { return m_Size; } // Function that returns our size
- private:
- void ReAlloc(size_t newCapacity)
- {
- // Steps in order
- // 1. Needs to allocate a new block of memory
- // 2. Copy/move old elements into new block, preferably move
- // 3. Delete old block
- // Want to use versions of new and delete that don't call the constructor or destructor
- T* newBlock = (T*)::operator new(newCapacity * sizeof(T)); // Raw pointers preferred when doing something low level like this
- // If downsizing instead of growing array, ensures array isn't overflowed -> done by only copying up to newCapacity size of elements
- if (newCapacity < m_Size)
- m_Size = newCapacity;
- for (size_t i = 0; i < m_Size; i++)
- newBlock[i] = std::move(m_Data[i]); // For more complex types like classes (vs primitives likes ints), can't use memcpy
- for (size_t i = 0; i < m_Size; i++)
- m_Data[i].~T();
- ::operator delete(m_Data, m_Capacity * sizeof(T)); // Deletes old block
- m_Data = newBlock; // Assign data to new block
- m_Capacity = newCapacity; // Make sure our capacity matches new capacity
- }
- private:
- T* m_Data = nullptr; // Pointer to whatever our type is, nullptr as default so code is easier to debug
- // Need those so you're not reallocating memory as often
- size_t m_Size = 0; // Number of elements in our vector
- size_t m_Capacity = 0; // How much memory have we allocated, how much could we store without having to reallocate
- };
Advertisement
Add Comment
Please, Sign In to add comment