Comparing push_back() and emplace_back() in C++

Comparing push_back() and emplace_back() in C++

Push_back() vs. emplace_back(): What's the difference in C++?

·

3 min read

Here's an improved version of your blog post:


Understanding emplace_back in C++: A Simple Guide

When working with C++ vectors, adding new elements is a common task. Two popular methods for doing this are push_back and emplace_back. While they might seem similar at first glance, emplace_back has some unique advantages that make it a powerful tool in certain situations.

What Makes emplace_back Special?

The emplace_back function is designed to add a new element directly to the end of a vector. What sets it apart is that it constructs the element in place, right in the vector's memory. This means there's no need to create a temporary object first, which can then be copied or moved into the vector. This can save both time and memory, especially when working with complex objects.

A Real-World Example: String Hashing

Let's look at a practical example inspired by a common problem in competitive programming: finding duplicate strings in an array using string hashing.

int compute_hasing(string const& s) {
    int n = s.size();
    int hash = 0;
    int p = 31;
    int power = 1;
    int mod = 1e9 + 9;
    for (int i = 0; i < n; i++) {
        hash = (hash + (s[i] - 'a' + 1) * power) % mod;
        power = (power * p) % mod;
    }
    return hash;
}

void findHashForEachString(vector<string> const& v) {
    vector<pair<int, int>> p;
    int n = v.size();
    for (int i = 0; i < n; i++) {
        p.push_back(make_pair(compute_hasing(v[i]), i));
    }
    sort(p.begin(), p.end());
    vector<vector<string>> groups;
    for (int i = 0; i < n; i++) {
        if (i == 0 || p[i].first != p[i - 1].first) {
            groups.emplace_back();
        }
        groups.back().push_back(v[p[i].second]);
    }
    for (const auto& group : groups) {
        for (const auto& str : group) {
            cout << str << " ";
        }
        cout << endl;
    }
}

How emplace_back Is Used in This Code

In this function, emplace_back() is used to create a new empty vector whenever a new group of strings with the same hash is found. Here's how it works:

  1. Grouping Strings by Hash: As we process each string, we compare its hash with the previous one. If the hash is different, we know we're dealing with a new group of strings.

  2. Creating a New Group: emplace_back() is called to add a new empty vector to the groups vector. This new vector will hold all the strings in the current group.

  3. Adding Strings to the Group: The current string is then added to this newly created vector.

This approach ensures that every time we encounter a new group of identical strings (based on their hashes), a new vector is created in groups to hold them.

emplace_back vs. push_back: What's the Difference?

To really understand emplace_back, let's compare it with push_back using a simple example:

struct Person {
    string name;
    int age;

    Person(string name, int age) : name(name), age(age) {
        cout << "Constructed Person: " << name << endl;
    }
};

int main() {
    vector<Person> people;

    Person john("John", 25);
    people.push_back(john);  // Creates a copy of 'john' and stores it in 'people'

    people.emplace_back("Doe", 30);  // Constructs 'Doe' directly in place

    return 0;
}

With push_back:

  • You first create a Person object named john.

  • When you call push_back(john), a copy of john is made and added to the vector people.

With emplace_back:

  • You pass the arguments "Doe" and 30 directly to emplace_back().

  • It constructs the Person object in place within the vector, without the need for a temporary object.

Key Points

  • push_back: Requires an existing object or temporary object to be moved or copied into the vector.

  • emplace_back: Constructs the object directly in place using the provided arguments, which can save resources and avoid unnecessary copies.