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; }