Kanishk

C++ Custom Vector (System-Level Design)

A production-grade implementation of std::vector focusing on Allocator Type Traits, the Rule of 5, and Strong Exception Guarantees. Designed to handle OS-level memory constraints and complex resource ownership.

The Problem

Standard dynamic arrays often fail to separate allocation from construction, leading to severe performance penalties when dealing with non-trivial objects. Furthermore, naive reallocations can corrupt system state if an exception is thrown mid-copy.

Allocator Paradigm

Uses std::allocator_traits to meticulously decouple raw memory allocation from object initialization, preventing default-constructor overhead.

Exception Safety

During reallocation, if an object throws, the buffer strictly rolls back. Implements std::move_if_noexcept to fall back to copy-semantics for risky types.

The Rule of 5

Strictly enforces Destructor, Copy/Move Constructors, and Copy/Move Assignments via the idiomatic copy-and-swap technique to ensure leak-free operations.

Iterator Compatibility

Provides `begin()`, `end()`, `cbegin()`, and `cend()` pointers satisfying requirements for range-based for-loops and standard library `<algorithm>` headers.

The Implementation

vector_impl.hpp
cpp
#include <iostream>
#include <memory>
#include <algorithm>
#include <utility>
#include <stdexcept>

template <typename T, typename Allocator = std::allocator<T>>
class Vector {
private:
    T* data_ = nullptr;
    size_t sz_ = 0;
    size_t cap_ = 0;
    Allocator alloc_;

    using AllocTraits = std::allocator_traits<Allocator>;

    void reallocate(size_t new_cap) {
        T* new_data = AllocTraits::allocate(alloc_, new_cap);
        size_t constructed = 0;
        
        try {
            // Strong Exception Guarantee: Move if noexcept, else copy.
            for (size_t i = 0; i < sz_; ++i) {
                AllocTraits::construct(alloc_, &new_data[i], std::move_if_noexcept(data_[i]));
                constructed++;
            }
        } catch (...) {
            // Rollback on construction failure
            for(size_t i = 0; i < constructed; ++i) {
                AllocTraits::destroy(alloc_, &new_data[i]);
            }
            AllocTraits::deallocate(alloc_, new_data, new_cap);
            throw;
        }

        // Destroy old data
        for (size_t i = 0; i < sz_; ++i) {
            AllocTraits::destroy(alloc_, &data_[i]);
        }
        
        if (data_) {
            AllocTraits::deallocate(alloc_, data_, cap_);
        }
        
        data_ = new_data;
        cap_ = new_cap;
    }

public:
    // Iterator Interface Support
    using iterator = T*;
    using const_iterator = const T*;
    
    iterator begin() { return data_; }
    iterator end() { return data_ + sz_; }
    const_iterator cbegin() const { return data_; }
    const_iterator cend() const { return data_ + sz_; }

    // --- Rule of 5 ---
    Vector() noexcept = default;
    
    // 1. Destructor
    ~Vector() {
        for (size_t i = 0; i < sz_; ++i) {
            AllocTraits::destroy(alloc_, &data_[i]);
        }
        if (data_) AllocTraits::deallocate(alloc_, data_, cap_);
    }

    // 2. Copy Constructor
    Vector(const Vector& other) : sz_(other.sz_), cap_(other.sz_) {
        data_ = AllocTraits::allocate(alloc_, cap_);
        for(size_t i = 0; i < sz_; ++i) {
            AllocTraits::construct(alloc_, &data_[i], other.data_[i]);
        }
    }

    // 3. Move Constructor
    Vector(Vector&& other) noexcept : data_(other.data_), sz_(other.sz_), cap_(other.cap_) {
        other.data_ = nullptr;
        other.sz_ = 0;
        other.cap_ = 0;
    }

    // 4. Copy Assignment
    Vector& operator=(const Vector& other) {
        if (this != &other) {
            Vector temp(other); // Copy-and-swap idiom
            std::swap(data_, temp.data_);
            std::swap(sz_, temp.sz_);
            std::swap(cap_, temp.cap_);
        }
        return *this;
    }

    // 5. Move Assignment
    Vector& operator=(Vector&& other) noexcept {
        if (this != &other) {
            for (size_t i = 0; i < sz_; ++i) AllocTraits::destroy(alloc_, &data_[i]);
            if (data_) AllocTraits::deallocate(alloc_, data_, cap_);
            
            data_ = std::exchange(other.data_, nullptr);
            sz_ = std::exchange(other.sz_, 0);
            cap_ = std::exchange(other.cap_, 0);
        }
        return *this;
    }

    void push_back(const T& value) {
        if (sz_ == cap_) {
            reallocate(cap_ == 0 ? 1 : cap_ * 2);
        }
        AllocTraits::construct(alloc_, &data_[sz_], value);
        sz_++;
    }

    void push_back(T&& value) {
        if (sz_ == cap_) {
            reallocate(cap_ == 0 ? 1 : cap_ * 2);
        }
        AllocTraits::construct(alloc_, &data_[sz_], std::move(value));
        sz_++;
    }
};
/projects/custom-vector
system_status:active