Home | About | Blog | Projects | Contact

Custom vector<T> class

Posted: July 20, 2025

Overview

I built a custom vector<T> class in C++ as a way to deepen my understanding of memory management, object lifetimes, and template-based container design. I wanted to reinforce my knowledge of what happens under the hood – memory allocation, how constructors and destructors are managed, and why rules like the Rule of 3 exist.

Focus

I also ensured everything worked for both primitive and complex types (std::string), with zero memory leaks

What I got out of it

Code


        #include "vector.hpp"

        using namespace std;

        int main(void) {

            MyVector <int>vec;
            MyVector <string>v;

            vec.push_back(100);
            vec.push_back(20);
            vec.push_back(40);
            for (size_t i=0; i< vec.get_size(); i++) cout << vec[i] << endl;

            cout << "\nResizing to 7" << endl;

            vec.resize(7);
            for (size_t i=0; i< vec.get_size(); i++) cout << vec[i] << endl;
            int tmp = vec.get_size();
            int tmp1 = vec.get_capacity();
            cout << "SIZE: " << tmp << " CAPACITY: " << tmp1 << endl;

            cout << "\nFINDING 20\n";

            vec.find(20);
            for (size_t i=0; i< vec.get_size(); i++) cout << vec[i] << endl;

            tmp = vec.get_size();
            tmp1 = vec.get_capacity();
            cout << "SIZE: " << tmp << " CAPACITY: " << tmp1 << endl;

            return 0;
        }
    

This is just random runtime I used to test my class methods as I developed the class. Below is the real development.


        #ifndef VECTOR_HPP
        #define VECTOR_HPP

        #include <iostream>
        using namespace std;

        template <typename T>
        class MyVector {
            private:
                T* Data;
                int Size;
                int Capacity;
            public:
                MyVector() {
                    Size = 0;
                    Capacity = 1;
                    Data = new T[1];
                    cout << "MyVector Constructor Called.." << endl;
                };

                //destructor
                ~MyVector() {
                    delete[] Data;
                    Data = nullptr;
                    Size = 0;
                    Capacity = 0;
                    cout << "MyVector Destructor Called.." << endl;
                };

                //copy constructor
                MyVector(const MyVector& other) : Size(other.Size), Capacity(other.Capacity) { 
                    Data = new T[Capacity * sizeof(T)];
                    for (size_t i = 0; i < Size; ++i){
                        Data[i] = other.Data[i];
                    }
                }

                //copy assignment operator
                MyVector& operator=(const MyVector& other){
                    if (this != other){
                        delete[] Data;
                        Size = other.Size;
                        Capacity = other.Capacity;
                        Data = new T[Capacity * sizeof(T)];
                        for (size_t i = 0; i<Size; ++i){
                            Data[i] = other.Data[i];
                        }
                    }
                    return *this;
                }

                void push_back(T val) {
                    if (Size == Capacity){
                        size_t newCapacity = (Capacity == 0) ? 1 : Capacity * 2;
                        T* newData = new T[(newCapacity * sizeof(T))];

                        for(size_t i = 0; i<Size; ++i)
                            newData[i] = Data[i];

                        delete[] Data;

                        Data = newData;
                        Capacity = newCapacity;
                    }
                    Data[Size++]=val;          
                }

                void pop_back(){
                    if (Size == 0) {
                        cout << "\n\nVECTOR EMPTY. CAN'T POP.\n\n";
                        return;
                    }
                    T* newData = new T[(Capacity*sizeof(T))];
                    for(size_t i = 0; i < Size - 1; ++ i)
                        newData[i] = Data[i];
                    delete[] Data;

                    Data = newData;
                    Size--;
                }

                void insert(size_t index, T val){
                    if (Size == Capacity){
                        size_t newCapacity = (Capacity == 0) ? 1 : Capacity * 2;
                        T* newData = new T[(newCapacity * sizeof(T))];

                        for(size_t i = 0; i<Size; ++i)
                            newData[i] = Data[i];

                        delete[] Data;

                        Data = newData;
                        Capacity = newCapacity;
                    }
                    T* buffer = new T(Capacity*sizeof(T));
                    Size++;
                    for (size_t i=0; i<index; i++){
                        buffer[i] = Data[i];
                    }
                    buffer[index] = val;
                    for(size_t i = index; i < Size; i++) {
                        buffer[i+1] = Data[i];
                    }
                    delete[] Data;
                    Data = buffer;
                }

                void clear(){
                    delete[] Data;
                    Size = 0;
                    Capacity = Capacity;
                    Data = new T[Capacity];
                }

                void reserve(size_t addition){
                    size_t newCapacity = Capacity + addition;
                    T* newData = new T[(newCapacity * sizeof(T))];

                    for(size_t i = 0; i<Size; ++i)
                        newData[i] = Data[i];

                    delete[] Data;

                    Data = newData;
                    Capacity = newCapacity;
                }

                void resize(size_t newSize){
                    T* newData = new T[(newSize * sizeof(T))];
                    if (newSize > Size){
                        size_t i;
                        for(i = 0; i<Size; i++){
                            newData[i] = Data[i];
                        }
                        for(i; i<newSize; i++){
                            newData[i] = 0x00;
                        }
                    } else {
                        for(size_t i = 0; i<newSize; i++){
                            newData[i] = Data[i];
                        }
                    } 
                        
                    delete[] Data;
                    Data = newData;
                    Size = newSize;
                    Capacity = newSize; 
                }

                void shrink_to_fit(){
                    if (Capacity > Size){
                        T* newData = new T[(Size * sizeof(T))];
                        for(size_t i = 0;i<Size; ++i) newData[i] = Data[i];
                        delete[] Data;
                        Data = newData;
                        Capacity = Size;
                    } else {
                        cout << "Vector Cannot Be Shrunk" << endl;
                    }
                }

                void swap(size_t index1, size_t index2){
                    T buffer1 = Data[index1];
                    T buffer2 = Data[index2];

                    Data[index1] = buffer2;
                    Data[index2] = buffer1;
                }

                void find(T target){
                    
                    bool targetFound;
                    int targetIndex;
                    for (size_t i = 0; i < Size; i++){
                        int tmp = Data[i];
                        if(tmp == target) {
                            targetFound = true;
                            targetIndex = i;
                            break;
                        }
                    }
                    if (!targetFound){
                        cout << "\n\nTARGET NOT FOUND.\n";
                        return;
                    }
                    T* newData = new T[Capacity * sizeof(T)];

                    Size--;
                    for (size_t i=0; i<targetIndex; i++){
                        newData[i] = Data[i];
                    }
                    for(size_t i = targetIndex; i < Size; i++) {
                        newData[i] = Data[i+1];
                    }
                    delete[] Data;
                    Data = newData;

                }

                T begin() {return Data[0];}
                T end() {return Data[Size - 1];}
                T& operator[](int i) {return Data[i];}
                int get_size() const {return Size;}
                int get_capacity() const {return Capacity;} 
        };

        #endif
    

← Back to blog home