push_back vs emplace_back: qual a diferença e o que escolher para iniciantes?

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: por que isso é importante?

\n\n

Você já se perguntou por que existem duas maneiras de adicionar um elemento ao final de um vector em C++? Afinal, por que reinventar a roda se existe o já consolidado push_back? A resposta está no desempenho e na flexibilidade do C++ moderno.

\n\n

No Stack Overflow, essa pergunta acumulou milhares de visualizações e centenas de comentários. E não é por acaso: entender a diferença entre push_back e emplace_back não é apenas um conhecimento acadêmico, mas uma habilidade prática que pode acelerar seu código em 2 a 3 vezes. Vamos entender de uma vez por todas.

\n\n

Diferença básica: o que dizem as documentações?

\n\n

Vamos começar pelo simples. Ambos os métodos adicionam um elemento ao final do contêiner (vector, list, queue). Mas eles fazem isso de maneiras diferentes:

\n\n
    \n
  • push_back — recebe um objeto pronto (por valor, referência ou referência rvalue) e copia/move esse objeto para o contêiner.
  • \n
  • emplace_back — recebe os argumentos para o construtor do objeto e cria o objeto diretamente dentro do contêiner (construção in-place).
  • \n
\n\n

Parece abstrato? Vamos ver o código.

\n\n

Exemplo prático: criando uma classe

\n\n

Vamos imaginar que temos uma classe simples Person:

\n\n
#include <iostream>\n#include <vector>\n#include <string>\n\nclass Person {\npublic:\n    std::string name;\n    int age;\n\n    // Construtor\n    Person(const std::string& n, int a) \n        : name(n), age(a) {\n        std::cout << "Construtor chamado para " << name << std::endl;\n    }\n\n    // Construtor de cópia\n    Person(const Person& other) \n        : name(other.name), age(other.age) {\n        std::cout << "Construtor de cópia para " << name << std::endl;\n    }\n\n    // Construtor de movimento\n    Person(Person&& other) noexcept\n        : name(std::move(other.name)), age(other.age) {\n        std::cout << "Construtor de movimento para " << name << std::endl;\n    }\n};
\n\n

Agora vamos ver o que acontece ao usar diferentes métodos.

\n\n

Opção 1: push_back com objeto temporário

\n\n
int main() {\n    std::vector<Person> people;\n    people.push_back(Person("Alice", 25));\n    return 0;\n}
\n\n

Saída:

\n
Construtor chamado para Alice\nConstrutor de movimento para Alice
\n\n

O que aconteceu? Primeiro, um objeto temporário Person é criado (primeira chamada do construtor). Em seguida, esse objeto é movido para o vector (construtor de movimento). Duas chamadas — trabalho em dobro.

\n\n

Opção 2: push_back com objeto pronto

\n\n
int main() {\n    std::vector<Person> people;\n    Person alice("Alice", 25);\n    people.push_back(alice);  // alice — lvalue\n    return 0;\n}
\n\n

Saída:

\n
Construtor chamado para Alice\nConstrutor de cópia para Alice
\n\n

Aqui vemos uma cópia, não um movimento. Se o objeto for grande (contendo, por exemplo, arrays dinâmicos), a cópia pode ser cara.

\n\n

Opção 3: emplace_back

\n\n
int main() {\n    std::vector<Person> people;\n    people.emplace_back("Bob", 30);  // Passamos os argumentos para o construtor\n    return 0;\n}
\n\n

Saída:

\n
Construtor chamado para Bob
\n\n

Apenas uma chamada de construtor! O objeto Person é criado diretamente dentro da memória do vector, sem cópias ou movimentos intermediários.

\n\n

Desempenho: quando a diferença é crítica?

\n\n

Para tipos simples (int, double, ponteiros), a diferença é imperceptível — o compilador otimiza tudo. Mas para objetos complexos, a diferença pode ser significativa:

\n\n
    \n
  • Objetos com construtor de cópia pesado (por exemplo, std::string, contêineres

Blogs

Book Recommendations