MyVector.h
#pragma once
template<typename MyVector>
class MyVectorIterator
{
public:
using ValueType = typename MyVector::ValueType;
using PointerType = ValueType*;
using ReferenceType = ValueType&;
public:
MyVectorIterator(PointerType ptr)
:m_Ptr(ptr) {}
MyVectorIterator& operator++()
{
m_Ptr++;
return *this;
}
MyVectorIterator operator++(int)
{
MyVectorIterator iterator = *this;
++(*this);
return iterator;
}
MyVectorIterator& operator--()
{
m_Ptr--;
return *this;
}
MyVectorIterator operator--(int)
{
MyVectorIterator iterator = *this;
--(*this);
return iterator;
}
ReferenceType operator[](int index)
{
return *(m_Ptr + index);
}
PointerType operator->()
{
return m_Ptr;
}
ReferenceType operator*()
{
return *m_Ptr;
}
ReferenceType operator*(int)
{
return *m_Ptr;
}
bool operator==(const MyVectorIterator& other) const
{
return m_Ptr == other.m_Ptr;
}
bool operator!=(const MyVectorIterator& other) const
{
return !(*this == other);
}
private:
PointerType m_Ptr;
};
template<typename T>
class MyVector
{
public:
using ValueType = T;
using Iterator = MyVectorIterator<MyVector<T>>;
public:
MyVector()
{
//allocate 2 elements
ReAlloc(2);
}
void PushBack(const T& value)
{
if (m_Size >= m_Capacity)
{
ReAlloc(m_Capacity * 2);//double it.
}
m_Data[m_Size] = value;
m_Size++;
}
// to avoid data copy
void PushBack(T&& value)
{
if (m_Size >= m_Capacity)
{
ReAlloc(m_Capacity * 2);
}
m_Data[m_Size] = std::move(value);
m_Size++;
}
size_t Size() const { return m_Size; }
template<typename...Args>
T& EmplaceBack(Args&&... args)
{
if (m_Size >= m_Capacity)
{
ReAlloc(m_Capacity * 2);
}
// new() -> advanced tip of the day, for constructing objects in that place.
new(&m_Data[m_Size]) T(std::forward<Args>(args)...);//better than:m_Data[m_Size] = T(std::forward<Args>(args)...);
return m_Data[m_Size++];
}
void PopBack()
{
if (m_Size > 0)
{
m_Size--;
m_Data[m_Size].~T();
}
}
void Clear()
{
for (size_t i = 0; i < m_Size; i++)
{
m_Data[i].~T();
}
m_Size = 0;
}
const T& operator[] (size_t index) const
{
//size check for debug mode.
if (index >= m_Size)
{
__debugbreak();
}
return m_Data[index];
}
T& operator[] (size_t index)
{
//size check for debug mode.
if (index >= m_Size)
{
__debugbreak();
}
return m_Data[index];
}
~MyVector()
{
Clear();
std::cout << "class MyVector destroyed!\n";
//delete[] m_Data;
::operator delete(m_Data, m_Capacity * sizeof(T));//::operator delete won't call any destructors.
}
Iterator begin()
{
return Iterator(m_Data);
}
Iterator end()
{
return Iterator(m_Data + m_Size);
}
private:
void ReAlloc(size_t newCapacity)
{
// allocate a new block of memory
//T* newBlock = new T[newCapacity];//raw pointer->to access memory as low level as we can.
T* newBlock = (T*) ::operator new(newCapacity * sizeof(T));//operator new() won't call any constructor.
//downsize or upsize
if (newCapacity < m_Size)
{
m_Size = newCapacity;
}
// move old elements into the new memory by using a for loop bc need to be
// hitting the copy constructor of all of these elements.
// memcpy() for simple types:int float.
for (size_t i = 0; i < m_Size; i++)
{
//newBlock[i] = std::move(m_Data[i]);
new (&newBlock[i]) T(std::move(m_Data[i]));
}
// clear
for (size_t i = 0; i < m_Size; i++)
{
m_Data[i].~T();
}
// delete the old block.
::operator delete(m_Data, m_Capacity * sizeof(T));// delete[] m_Data;
m_Data = newBlock;
m_Capacity = newCapacity;
}
private:
T* m_Data = nullptr;
size_t m_Size = 0;//the number of element.
size_t m_Capacity = 0;//how much memory we have allocated.
};
main.cpp
#include <iostream>
#include <string>
#include "MyVector.h"
// what does it actually take to write a vector class.
struct Vector3
{
float x = 0.0f, y = 0.0f, z = 0.0f;
int* m_MemoryBlock = nullptr;
Vector3() { m_MemoryBlock = new int[5]; }
Vector3(float scalar)
:x(scalar), y(scalar), z(scalar)
{
m_MemoryBlock = new int[5];
}
Vector3(float x, float y, float z)
:x(x), y(y), z(z)
{
m_MemoryBlock = new int[5];
}
Vector3(const Vector3& other) = delete;
// :x(other.x), y(other.y), z(other.z)
//{
// std::cout << "struct Vector3 Copied!\n";
//}
Vector3(Vector3&& other)
:x(other.x), y(other.y), z(other.z)
{
std::cout << "struct Vector3 Moved!\n";
m_MemoryBlock = other.m_MemoryBlock;
other.m_MemoryBlock = nullptr;
}
~Vector3()
{
std::cout << "struct Vector3 Destroyed\n";
delete[] m_MemoryBlock;
}
Vector3& operator=(const Vector3& other) = delete;
//{
// std::cout << "struct Vector3 operator= Copied!\n";
// x = other.x;
// y = other.y;
// z = other.z;
// return *this;
//}
Vector3& operator=(Vector3&& other)
{
std::cout << "struct Vector3 operator= Moved!\n";
m_MemoryBlock = other.m_MemoryBlock;
other.m_MemoryBlock = nullptr;
x = other.x;
y = other.y;
z = other.z;
return *this;
}
};
template<typename T>
void PrintMyVector(const MyVector<T>& vector)
{
for (size_t i = 0; i < vector.Size(); i++)
{
std::cout << vector[i] << std::endl;
}
std::cout << "-----------------------------" << std::endl;
}
template<>
void PrintMyVector(const MyVector<Vector3>& vector)
{
for (size_t i = 0; i < vector.Size(); i++)
{
std::cout << vector[i].x << ", " << vector[i].y << ", " << vector[i].z << std::endl;
}
std::cout << "-----------------------------" << std::endl;
}
int main(void)
{
MyVector<Vector3> data2;
data2.PushBack(Vector3(1.0f));
data2.PushBack(Vector3(2, 3, 4));
data2.PushBack(Vector3());
PrintMyVector(data2);
MyVector<Vector3> data3;
data3.EmplaceBack(1.0f);
data3.EmplaceBack(2, 3, 4);
data3.EmplaceBack();
PrintMyVector(data3);
MyVector<Vector3> data4;
data4.EmplaceBack(1.0f);
data4.EmplaceBack(2, 3, 4);
data4.EmplaceBack(1, 3, 4);
data4.EmplaceBack();
PrintMyVector(data4);
data4.PopBack();
data4.PopBack();
PrintMyVector(data4);
data4.EmplaceBack(5, 2, 0);
data4.EmplaceBack(1, 7, 9);
PrintMyVector(data4);
data4.Clear();
PrintMyVector(data4);
data4.EmplaceBack(5, 2, 0);
data4.EmplaceBack(1, 7, 9);
PrintMyVector(data4);
PrintMyVector(data4);
MyVector<int> data5;
data5.PushBack(5);
data5.EmplaceBack(2);
data5.EmplaceBack(2);
data5.EmplaceBack(2);
data5.EmplaceBack(2);
data5.EmplaceBack(2);
PrintMyVector(data5);
data5.PopBack();
PrintMyVector(data5);
data5.Clear();
std::cin.get();
}