Home | About | Blog | Projects | Contact
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.
operator new[]I also ensured everything worked for both primitive and complex types (std::string), with zero memory leaks
#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