HOWTO · C++

Copiar um objeto Vector Container em C++

Este artigo demonstra como copiar um objeto de recipiente de vetor em C++.

Nesta página

Para copiar um std::vector em C++, use o construtor de cópia ao criar um novo vetor e a atribuição de cópia para substituir o conteúdo de um vetor existente. Para copiar um intervalo específico, use o construtor por intervalo. std::copy funciona quando o intervalo de destino já tem espaço suficiente, enquanto std::back_inserter acrescenta elementos ao destino. Essas operações copiam os elementos de acordo com seus tipos; elas não copiam recursivamente os objetos acessados por ponteiros.

Copiar um vetor com o construtor de cópia ou o operador de atribuição

A inicialização por cópia cria um novo vetor a partir de outro existente: std::vector<int> copy = source;. A inicialização direta faz o mesmo usando parênteses: std::vector<int> copy(source);. Se o destino já existir, use a atribuição: destination = source;. A atribuição substitui os elementos do destino. Inclua <vector> para usar essas formas.

Execute este exemplo:

O código e os rótulos ou saídas exibidos nesta seção permanecem em inglês.

#include <cstddef>
#include <iostream>
#include <vector>

int main() {
    const std::vector<int> source{2, 4, 6};
    std::vector<int> copy = source;
    std::vector<int> destination{9};
    destination = source;

    for (std::size_t i = 0; i < copy.size(); ++i) {
        if (i != 0) {
            std::cout << ' ';
        }
        std::cout << copy[i];
    }
    std::cout << '\n';
    for (std::size_t i = 0; i < destination.size(); ++i) {
        if (i != 0) {
            std::cout << ' ';
        }
        std::cout << destination[i];
    }
    std::cout << '\n';
}
2 4 6
2 4 6

Este programa imprime cada vetor copiado em sua própria linha. Os elementos std::vector<int> são valores; por isso, alterar um inteiro em um vetor não altera o inteiro correspondente no outro.

Copiar um intervalo com o construtor por intervalo de vector

O construtor por intervalo recebe um intervalo semiaberto de iteradores: o primeiro iterador é incluído e o segundo é excluído. Use parênteses para distinguir esse construtor da sintaxe de lista de inicializadores.

std::vector<int> copy(source.begin(), source.end()); cria uma cópia de todo o intervalo. Para copiar um subintervalo, use std::vector<int> middle(source.begin() + 1, source.end() - 1); quando esses iteradores delimitarem os elementos desejados.

A primeira declaração copia todos os elementos; neste exemplo, a segunda copia o intervalo interno. Os dois iteradores devem delimitar um intervalo válido no mesmo vetor de origem. Essa é uma opção prática quando você precisa de um novo destino para copiar um vetor inteiro ou um subintervalo.

Copiar um intervalo com std::copy

std::copy grava em um intervalo de destino existente. Inclua <algorithm> e, antes de copiar, verifique se o destino tem pelo menos tantos elementos quanto o intervalo de origem. O algoritmo não aumenta o tamanho de um vetor por conta própria.

std::vector<int> destination(source.size()); seguido de std::copy(source.begin(), source.end(), destination.begin()); fornece um intervalo de destino grande o bastante para a origem.

Para copiar um subintervalo, passe os iteradores que marcam seu início e fim e forneça elementos de destino suficientes para ele. Os intervalos de origem e destino não podem se sobrepor de uma forma que viole os requisitos do algoritmo.

Acrescentar elementos copiados com std::back_inserter

Para acrescentar elementos e permitir que o vetor cresça conforme necessário, inclua <algorithm> e <iterator> e use std::back_inserter:

Por exemplo, std::vector<int> destination{10}; seguido de std::copy(source.begin(), source.end(), std::back_inserter(destination)); acrescenta os elementos da origem.

O valor 10 original permanece no início, seguido pelos valores da origem. Essa forma é útil quando o destino já contém elementos ou quando seu tamanho final não é definido antecipadamente.

Copiar elementos com um laço e push_back

Um laço deixa explícita a operação realizada em cada elemento. push_back adiciona cada valor copiado ao final, aumentando o conteúdo de um destino que já tenha elementos.

Um laço direto é for (const int& value : source) { destination.push_back(value); }, depois de declarar std::vector<int> destination;.

Para uma cópia comum, o construtor de cópia do vetor ou o construtor por intervalo costuma ser mais claro. Um laço é adequado quando cada elemento precisa ser transformado ou filtrado ao ser adicionado. Assim como nas operações padrão de vetor, o laço copia cada elemento de acordo com seu tipo.

Substituir ou inserir elementos no destino com assign e insert

Use assign(first, last) quando quiser substituir o destino por um intervalo. Use insert(position, first, last) quando quiser inserir o intervalo de origem em uma posição e preservar os outros elementos do destino.

Use destination.assign(source.begin(), source.end()); para substituir o conteúdo. Use destination.insert(destination.begin(), source.begin(), source.end()); para inserir o intervalo de origem no início.

A posição de inserção deve ser um iterador válido do destino. Se a inserção realocar o armazenamento do vetor, todos os iteradores, ponteiros e referências aos seus elementos serão invalidados. Se não houver realocação, ainda assim serão invalidados os iteradores e as referências na posição de inserção ou depois dela; os anteriores permanecem válidos. Depois da inserção, obtenha novamente os iteradores e as referências afetados.

Entender o que uma cópia de vetor duplica

Um std::vector armazena seus elementos de forma contígua e gerencia o armazenamento desses elementos. Ao copiar um vetor, cria-se outro vetor, com cópias de seus elementos. Em std::vector<int>, os valores inteiros são copiados; portanto, uma atribuição posterior a um elemento de um vetor não altera o valor no outro.

O comportamento para outros tipos de elemento depende da operação de cópia de cada tipo. Por exemplo, copiar um vetor de ponteiros brutos copia os valores dos ponteiros, mas não cria novos objetos apontados. Se os elementos forem ponteiros inteligentes, a cópia segue a semântica deles, como a propriedade compartilhada de std::shared_ptr. Portanto, ter vetores separados não significa necessariamente que os objetos acessados por seus elementos também sejam independentes.

Escolher um método para copiar um vetor

Escolha o construtor de cópia ou o construtor por intervalo para criar um novo vetor. Escolha a atribuição de cópia ou assign para substituir um destino existente. Use insert para adicionar um intervalo em uma posição e std::copy com std::back_inserter para acrescentar elementos enquanto o destino cresce. Use std::copy com um destino dimensionado quando quiser gravar em um intervalo de elementos já alocado. Um laço com push_back é útil quando a cópia também exige operações personalizadas em cada elemento.

Para fazer uma nova cópia completa, prefira o construtor de cópia; para selecionar elementos, use o construtor por intervalo. Em um vetor existente, escolha a atribuição ou assign para substituir o conteúdo, insert para colocar elementos em uma posição ou std::back_inserter para acrescentá-los.