Search code examples
c++performancepush-back

What explains so many moves and destructions in this snippet


In order to understand the rule of five, I came up with this:

#include <iostream>
#include <vector>

class A
{
public:
A(int y)
{
    std::cout << "constructed\n";
    x = new int[y];
}
~A()
{
    std::cout << "destructed\n";
    delete[] x;
}
A(const A &other)
{
    std::cout << "copied\n";
    if (other.x != nullptr)
        x = new int[sizeof(other.x) / sizeof(other.x[0])];
}
A(A && other) :
    x(other.x)
{
    std::cout << "moved\n";
    other.x = nullptr;
}
A& operator=(A other)
{
    std::cout << "assignments\n";
    std::swap(x, other.x);
    return *this;
}
int *x;
};

class B
{
private:
std::vector<A> a;
public:
B()
{
    for (int i = 0; i < 5; i++)
        a.emplace_back(A(1));
    std::cout << "----------------------\n";
    for (int i = 0; i < 5; i++)
        a.push_back({ 2 });
}
};

int main()
{
B *b = new B();
std::cin.get();
}

If, instead, I use emplace_back and push_back 1 time each, I get this output, regardless of the order I call each method:

constructed
moved
destructed
----------------------
constructed
moved
destructed
moved
destructed

I accept that. However, if I use the code as wrote above, I get some tricky pattern:

constructed
moved
destructed
constructed
moved
destructed
moved
destructed
constructed
moved
moved
destructed
destructed
moved
destructed
constructed
moved
moved
moved
destructed
destructed
destructed
moved
destructed
constructed
moved
moved
moved
moved
destructed
destructed
destructed
destructed
moved
destructed
----------------------
constructed
moved
destructed
constructed
moved
moved
moved
moved
moved
moved
destructed
destructed
destructed
destructed
destructed
destructed
moved
destructed
constructed
moved
destructed
constructed
moved
destructed
constructed
moved
moved
moved
moved
moved
moved
moved
moved
moved
destructed
destructed
destructed
destructed
destructed
destructed
destructed
destructed
destructed
moved
destructed

What explains so many "moved" and "destructed"? Also, can we state that emplace_back is better than push_back just by this output?


Solution

  • I think this output is not an error

    if std::vector's size is full and you try to push, vector will allocate more memory and move object in original memory block to new memory block and destroy original memory block.

    so you can use std::vector.reserve()

    #include <iostream>
    #include <vector>
    
    class A
    {
    public:
    A(int y)
    {
        std::cout << "constructed\n";
        x = new int[y];
    }
    ~A()
    {
        std::cout << "destructed\n";
        delete[] x;
    }
    A(const A &other)
    {
        std::cout << "copied\n";
        if (other.x != nullptr)
            x = new int[sizeof(other.x) / sizeof(other.x[0])];
    }
    A(A && other) :
        x(other.x)
    {
        std::cout << "moved\n";
        other.x = nullptr;
    }
    A& operator=(A other)
    {
        std::cout << "assignments\n";
        std::swap(x, other.x);
        return *this;
    }
    int *x;
    };
    
    class B
    {
    private:
    std::vector<A> a;
    public:
    B()
    {
        //reserve
        a.reserve(5);
    
        for (int i = 0; i < 5; i++)
            a.emplace_back(A(1));
        std::cout << "----------------------\n";
       // for (int i = 0; i < 5; i++)
       //    a.push_back({ 2 });
    }
    };
    
    int main()
    {
    B *b = new B();
    std::cin.get();
    }
    

    this output will be like this

    constructed
    moved
    destructed
    constructed
    moved
    destructed
    constructed
    moved
    destructed
    constructed
    moved
    destructed
    constructed
    moved
    destructed