0

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?

  • 2
    `I wrote the following code, but I am having a hard time understanding it.` Now that's just silly. – hungrykoala Jun 14 '18 at 02:25
  • 2
    A review of how `std::vector` manages its contents will answer all of your questions. The detailed explanation of all the evil optimizations that `std::vector` does to perform reallocation as efficiently as possible, using move semantics, is not something that can be fully provided in a short few paragraphs on stackoverflow.com. – Sam Varshavchik Jun 14 '18 at 02:26
  • 4
    `new int[sizeof(other.x) / sizeof(other.x[0])]` Sigh. Couldn't people just stop telling people to use `sizeof` on "array"? (I mean both actual array and anything mistaken as array) It's error-prone. –  Jun 14 '18 at 02:36
  • Your `x` member is uninitialized in your copy constructor if `other.x` is a `nullptr`. Thus `delete [] x;` in that case leads to undefined behavior. Maybe you should first clean up your example to not have these basic errors, and then retest. – PaulMcKenzie Jun 14 '18 at 02:57
  • 1
    The output is meaningless because your program causes undefined behaviour – M.M Jun 14 '18 at 03:31
  • `a.emplace_back(A(1));` Note that this is not how you're supposed to use `emplace_back`. The point of `emplace_back` is to construct `A` in-place, not to construct it at the call-site. Thus, you should write `a.emplace_back(1);` – Justin Jun 14 '18 at 04:31
  • Excuse my curiosity, why the move semantics looks so similar to a copy constructor ? – Hello Everyone Jun 14 '18 at 04:44

1 Answers1

-1

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