I am learning c++ and try to understand the object lifetime of the move constructor and copy constructor. I am using g++ 9.2.0, c++17 in vscode. I created a struct Card holding a string & int, and a struct Deck that holds cards in vector<Card>. When executing build_deck member function loop over string and int to add Card to the vector in Deck by embrace_back, the output shows that each loop behaved differently. How can this happen?
The "extra" copy constructions and destructions occur when std::vector reallocates.
When std::vector runs out of room it allocates a new buffer and copies (generally) the contents of the old into the new. As part of the process the elements in the old buffer are copied and then destroyed.
std::vector will not move from its elements unless the element type's move constructor is non-throwing. Therefore declare move constructors and move assignment operators (and swap) noexcept when possible.
You could have found this by yourself by using a debugger.
Thanks for the reply!
I found that when I put noexcept inside the move constructor, the Cards are now moved instead of copied.
Also for clarification: in my case, during putting 4, 6,7,8th card into the Deck, the vector found that the next memory is open to use and thus no reallocation happened which is a happy accident? Is this what happened?
Finally, may I ask a follow-up question regarding this case: it seems the Card added whenever reallocation happens is the default Blank card (the four Blank Cards destroy before heart and spade). How can this happen and how to make it correctly add the right suit (club and diamond)? This problem exists even after adding noexcept to move and/or copy constructor.
Thanks for the fast reply, it worked after trying your code. However, I am still wondering why my code does not work as I intended while your code does. May I ask why using the initializer list in the copy and move constructor can solve the issue I had before?
If the initializer list is not used, the object is first default constructed, then values are assigned. The values specified in the initializer list are assigned as the object is constructed.
Consider:
1 2 3 4
int a{};
a = 7;
int b {7};
a and b both get assigned the value 7 - but a first has the value 0 assigned then the value 7. b has 7 assigned directly.
The point of @seeplus's post (I think) is that neither your copy or move constructors assign the members of the newly-constructed object. All your copy or move constructor does is print "copy" (or "move"), but no copy is actually made.
Look at his code above for an example of how to define the copy/move constructors properly.