Capítulo 05: Coleções Contíguas: std::array, std::vector e std::span
Capítulo 05: Coleções Contíguas: std::array, std::vector e std::span
Especialização em Sistemas com C++ • C++20 & C++23 • RAII, Smart Pointers, Templates, Concorrência e Alta Performance
🗺️ Mapa Conceitual de Coleções Contíguas e Visões de Memória
flowchart TD
Memoria["Memória Contígua"] --> Stack["Stack (Tamanho Fixo em Compile-Time)"]
Memoria --> Heap["Heap Dinâmico (Tamanho Expansível em Runtime)"]
Stack --> StdArray["std::array<T, N> (Sem Overhead de Alocação)"]
Heap --> StdVector["std::vector<T> (Capacidade, Tamanho e Realocação)"]
subgraph VIEW_LAYER["Camada de Abstração Zero-Copy (C++20)"]
StdSpan["std::span<T> (Ponteiro + Tamanho - Non-Owning View)"]
end
StdArray --> VIEW_LAYER
StdVector --> VIEW_LAYER
RawArray["C-Style Array T[]"] --> VIEW_LAYER
VIEW_LAYER --> Algorithms["Funções e Algoritmos Unificados Sem Cópia"]
style Memoria fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style VIEW_LAYER fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
style Algorithms fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
🏛️ 1. O Triunfo da Localidade Espacial de Referência
Em computação de alto desempenho, a eficiência de um algoritmo é dominada pela hierarquia de memória da máquina física (Registradores -> L1 Cache -> L2 Cache -> L3 Cache -> RAM principal). Acessar dados contíguos na memória permite que o hardware execute pre-fetching automático de linhas de cache de 64 bytes.
Por essa razão, contêineres contíguos (std::array e std::vector) superam estruturas baseadas em nós (como std::list ou árvores balanceadas) por ordens de magnitude em tarefas do mundo real, mesmo quando a complexidade assintótica teórica sugeriria o contrário.
⚙️ 2. std::array vs std::vector: Stack vs Heap
2.1 std::array<T, N> (Stack Memory)
- Tamanho
Nconhecido estaticamente em tempo de compilação. - Não aloca nenhum byte no Heap do sistema operacional.
- Custo de inicialização e destruição instantâneo.
- Suporta todas as operações de segurança da STL, como
.at(index)para verificação de limites (bounds checking).
2.2 std::vector<T> (Heap Memory)
- O contêiner de propósito geral por excelência no C++.
- Gerencia um buffer contíguo alocado dinamicamente através de três ponteiros internos:
begin,endeend_of_storage. - A Regra de Ouro: Sempre utilize
.reserve(capacidade)quando souber a estimativa de elementos prévia. Isso elimina realocações repetidas de memória (malloc/free) e cópias caras de objetos. emplace_back: Constrói o objeto diretamente no buffer do vetor, eliminando a criação de objetos temporários intermediários.
📦 3. std::span (C++20): A Visão Uniforme de Memória
Antes do C++20, se você quisesse escrever uma função de processamento de áudio ou telemetria que operasse sobre buffers de floats, você era forçado a escolher entre:
- Receber
const std::vector<float>&(impossibilitando o uso comstd::arrayou vetores legados do C). - Criar sobrecargas para cada contêiner.
- Passar o par inseguro
const float* ptr, size_t count.
O std::span<T> resolve esse problema definitivamente:
- É uma referência não proprietária (non-owning view) contendo apenas o ponteiro base e a quantidade de elementos.
- Não realiza nenhuma alocação dinâmica.
- Suporta sub-intervalos através de
.subspan(offset, count). - É compatível transparentemente com
std::vector,std::array,std::stringou arrays estáticos brutos.
#include <span>
#include <iostream>
void processar_telemetria(std::span<const int> dados) {
std::cout << "Processando " << dados.size() << " elementos na memoria.\n";
for (int v : dados) {
// Acesso contíguo otimizado
}
}
📊 4. Matriz Comparativa: Contêineres Contíguos
| Característica | std::array<T, N> |
std::vector<T> |
std::span<T> (C++20) |
|---|---|---|---|
| Local de Armazenamento | Stack (ou embutido na struct) | Heap (via alocador dinâmico) | Nenhum (apenas aponta para memória externa) |
| Tamanho Mutável? | Não (fixo em compile-time) | Sim (cresce dinamicamente) | Não (reflete o buffer apontado) |
| Overhead de Espaço | 0 bytes além dos dados | 24 bytes (3 ponteiros de 64 bits) | 16 bytes (ponteiro + tamanho) |
| Propriedade (Ownership) | Possui os dados | Possui os dados | Não possui (visão temporária) |
🔬 5. Laboratório de Arquitetura: Arenas de Memória e PMR (Polymorphic Memory Resources)
Em sistemas de tempo real rígido (Hard Real-Time), invocar o alocador padrão do sistema operacional (malloc) dentro de um std::vector é proibido devido ao risco de contenção de travas do kernel e latência indeterminística.
5.1 O Namespace std::pmr (C++17/C++20)
O C++ moderno padronizou alocadores polimórficos no namespace std::pmr. Com ele, o tipo do vetor não muda com o tipo do alocador:
#include <vector>
#include <memory_resource>
#include <array>
// Buffer estático pré-alocado na Stack (Zero chamadas ao Kernel!)
std::array<std::byte, 1024 * 64> stack_pool;
std::pmr::monotonic_buffer_resource pool(stack_pool.data(), stack_pool.size());
// Vetor que utiliza a memória estática da Stack como sua Heap privada:
std::pmr::vector<int> dados(&pool);
dados.reserve(1000); // Alocação ultra-rápida via bump pointer em nanossegundos!
O uso de monotonic_buffer_resource reduz o tempo de alocação para poucas instruções de CPU (bump allocation), liberando todo o bloco de uma só vez na destruição do recurso de memória.