Implementação Completa de uma Classe de Array Dinâmico em C++: Uma Abordagem Detalhada

Construtores e Funções Membros Padrão

Construtor Padrão

O construtor inicializa os ponteiros internos para nullptr, representando um vetor vazio:

vector()
    : _data(nullptr)
    , _size(0)
    , _capacity(0)
{}

Embora seja possível omitir a lista de inicialização se valores padrão forem definidos na declaração dos membros, é recomendado mantê-la para clareza. Alternativamente, pode-se usar = default; para forçar o construtor padrão gerado pelo compilador:

vector() = default;

Isso garante comportamento consistente entre diferentes compiladores.

Construtor com Lista de Inicializadores

Permite criar um vetor diretamente a partir de uma lista de valores:

vector(std::initializer_list<T> init) {
    reserve(init.size());
    for (const auto& item : init) {
        push_back(item);
    }
}

Construtor por Inetrvalo de Iteradores

Oferece flexibilidade ao permitir inicialização a partir de qualquer contêiner iterável:

template<typename InputIt>
vector(InputIt first, InputIt last) {
    while (first != last) {
        push_back(*first++);
    }
}

Destrutor

Libera a memória alocada dinamicamente, evitando vazamentos:

~vector() {
    delete[] _data;
    _data = nullptr;
    _size = _capacity = 0;
}

Construtor de Cópia

Cria uma cópia profunda do objeto original:

vector(const vector& other) {
    reserve(other._size);
    for (size_t i = 0; i < other._size; ++i) {
        _data[i] = other._data[i];
    }
    _size = other._size;
}

Sobrecarga do Operador de Atribuição

Utiliza o idiom "copy-and-swap" para garantir segurança em caso de exceções:

vector& operator=(vector other) {
    swap(other);
    return *this;
}

void swap(vector& other) noexcept {
    std::swap(_data, other._data);
    std::swap(_size, other._size);
    std::swap(_capacity, other._capacity);
}

Iteradores

Os iteradores são implementados como ponteiros simples, facilitando acesso direto aos elementos:

using iterator = T*;
using const_iterator = const T*;

iterator begin() { return _data; }
iterator end() { return _data + _size; }
const_iterator begin() const { return _data; }
const_iterator end() const { return _data + _size; }

Operações de Modificação

Inserção no Final (push_back)

Adiciona um elemento ao final, redimensionando automaticamente se necessário:

void push_back(const T& value) {
    if (_size == _capacity) {
        size_t new_cap = (_capacity == 0) ? 4 : _capacity * 2;
        reserve(new_cap);
    }
    _data[_size++] = value;
}

Inserção em Posição Arbitrária (insert)

Insere um valor em uma posição específica, deslocando os elementos subsequentes:

iterator insert(iterator pos, const T& value) {
    size_t index = pos - _data;
    if (_size == _capacity) {
        size_t new_cap = (_capacity == 0) ? 4 : _capacity * 2;
        reserve(new_cap);
        pos = _data + index; // Atualiza o iterador após realocação
    }
    for (auto it = _data + _size; it != pos; --it) {
        *it = *(it - 1);
    }
    *pos = value;
    ++_size;
    return pos;
}

Remoção de Elemento (erase)

Remove um elemento em uma posição dada e retorna o próximo iterador válido:

iterator erase(iterator pos) {
    for (auto it = pos; it != _data + _size - 1; ++it) {
        *it = *(it + 1);
    }
    --_size;
    return pos;
}

Retornar o iterador seguinte permite loops seguros ao remover múltiplos elementos:

// Exemplo: remover todos os números pares
auto it = vec.begin();
while (it != vec.end()) {
    if (*it % 2 == 0) {
        it = vec.erase(it);
    } else {
        ++it;
    }
}

Gerenciamento de Capacidade

Reserva de Memória (reserve)

Altera a capacidade sem afetar o tamanho lógico:

void reserve(size_t new_capacity) {
    if (new_capacity <= _capacity) return;

    T* new_block = new T[new_capacity];
    for (size_t i = 0; i < _size; ++i) {
        new_block[i] = _data[i]; // Cópia elemento a elemento
    }
    delete[] _data;
    _data = new_block;
    _capacity = new_capacity;
}

Evita o uso de memcpy, que não invoca construtores e causaria falhas com tipos complexos como std::string.

Tamanho e Capacidade

Métodos simples para consulta:

size_t size() const { return _size; }
size_t capacity() const { return _capacity; }
bool empty() const { return _size == 0; }

Redimensionamento (resize)

Ajusta o número de elementos ativos:

void resize(size_t new_size, const T& value = T()) {
    if (new_size < _size) {
        _size = new_size;
    } else {
        reserve(new_size);
        while (_size < new_size) {
            _data[_size++] = value;
        }
    }
}

Limpeza (clear)

Remove todos os elementos sem liberar a memória alocada:

void clear() { _size = 0; }

Tags: vector C++ array dinâmico iteradores gerenciamento de memória

Publicado em 8-27 10:11