In this post, we’ll walk through a simplified implementation of a dynamic array class called MyVector. It mirrors many of the behaviors of std::vector while staying small enough to reason about and explain in an interview or learning context.
#include <cstddef>
#include <utility>
#include <stdexcept>
#include <iostream>
#include <string>
template<typename T>
class MyVector {
private:
T* data;
size_t capacity;
size_t length;
void resize() {
size_t newCapacity = (capacity == 0) ? 1 : capacity * 2;
T* newData = new T[newCapacity];
for(size_t i = 0; i < length; i++) {
newData[i] = std::move(data[i]);
}
delete[] data;
data = newData;
capacity = newCapacity;
}
public:
MyVector() : data(nullptr), capacity(0), length(0) {}
~MyVector() {
delete[] data;
}
MyVector(const MyVector& other) : data(nullptr), capacity(other.capacity), length(other.length) {
if(capacity > 0) {
data = new T[capacity];
for(size_t i = 0; i < length; i++) {
data[i] = other.data[i];
}
}
}
MyVector(MyVector&& other) noexcept
: data(other.data), capacity(other.capacity), length(other.length) {
other.data = nullptr;
other.capacity = 0;
other.length = 0;
}
MyVector& operator=(const MyVector& other) {
if(this != &other) {
T* newData = nullptr;
if(other.capacity > 0) {
newData = new T[other.capacity];
for(size_t i = 0; i < other.length; i++) {
newData[i] = other.data[i];
}
}
delete [] data;
data = newData;
capacity = other.capacity;
length = other.length;
}
return *this;
}
MyVector& operator=(MyVector&& other) noexcept {
if(this != &other) {
delete [] data;
data = other.data;
capacity = other.capacity;
length = other.length;
other.data = nullptr;
other.capacity = 0;
other.length = 0;
}
return *this;
}
void push(const T& value) {
if(length == capacity) {
resize();
}
data[length++] = value;
}
void push(T&& value) {
if (length == capacity) {
resize();
}
data[length++] = std::move(value);
}
T& operator[](size_t index){
return data[index];
}
const T& operator[](size_t index) const {
return data[index];
}
size_t size() const {
return length;
}
size_t getCapacity() const {
return capacity;
}
void clear() {
length = 0;
}
T& at(size_t index) {
if(index >= length) {
throw std::out_of_range("Index out of range");
}
return data[index];
}
const T& at(size_t index) const {
if(index >= length) {
throw std::out_of_range("Index out of range");
}
return data[index];
}
void reserve(size_t newCapacity) {
if(newCapacity <= capacity) {
return;
}
T* newData = new T[newCapacity];
for(size_t i=0; i <length; i++) {
newData[i] = std::move(data[i]);
}
delete [] data;
data = newData;
capacity = newCapacity;
}
};
int main() {
MyVector<int> v;
v.push(10);
v.push(20);
v.push(30);
std::cout << "v size: " << v.size() << "\n";
std::cout << "v capacity: " << v.getCapacity() << "\n";
std::cout << "Elements:\n";
for (size_t i = 0; i < v.size(); i++) {
std::cout << v[i] << " ";
}
std::cout << "\n";
// Copy constructor
MyVector<int> v2 = v;
v2[0] = 100;
std::cout << "v2[0] after copy: " << v2[0] << "\n";
std::cout << "v[0] original: " << v[0] << "\n";
// Copy assignment
MyVector<int> v3;
v3 = v;
// Move constructor
MyVector<int> v4 = std::move(v3);
// Move assignment
MyVector<int> v5;
v5 = std::move(v4);
// at() with bounds checking
try {
std::cout << "v5.at(1): " << v5.at(1) << "\n";
std::cout << v5.at(10) << "\n"; // will throw
} catch (const std::out_of_range& e) {
std::cout << "Exception: " << e.what() << "\n";
}
// reserve()
MyVector<std::string> vs;
vs.reserve(5);
vs.push("hello");
vs.push("world");
std::cout << "vs capacity after reserve: " << vs.getCapacity() << "\n";
std::cout << "vs[0]: " << vs[0] << "\n";
// clear()
vs.clear();
std::cout << "vs size after clear: " << vs.size() << "\n";
return 0;
}
Code language: C++ (cpp)