Reputation:
I have a problem understanding the concept of vectors working with move semantics and objects. I'll put the code and explain you the problem exactly, because I really want to understand the logic behind this.
#include <iostream>
#include <vector>
#include <cstring>
#include "Mystring.h"
using namespace std;
class Mystring {
private:
char* str;
public:
Mystring();
Mystring(const char* s);
Mystring(const Mystring& source);
Mystring(Mystring&& source);
~Mystring();
Mystring& operator=(const Mystring& rhs);
Mystring& operator=(Mystring&& rhs);
void display() const;
int get_lenght() const;
const char* get_str() const;
};
int main() {
vector <Mystring> stooges_vec;
stooges_vec.push_back("Larry"); //11
stooges_vec.push_back("Moe"); //12
stooges_vec.push_back("Curly"); //13
return 0;
}
//One-args constructor
Mystring::Mystring(const char* s)
: str{ nullptr } {
if (s == nullptr) {
str = new char[1];
*str = '\0';
}
else {
str = new char[std::strlen(s) + 1];
std::strcpy(str, s);
}
}
//Copy constructor
Mystring::Mystring(const Mystring& source)
:str{ nullptr } {
str = new char[std::strlen(source.str) + 1];
std::strcpy(str, source.str);
}
//Move constructor
Mystring::Mystring(Mystring&& source)
:str{ source.str } {
source.str = nullptr;
std::cout << "Move constructor called." << std::endl;
}
//Destructor
Mystring::~Mystring() {
delete[] str;
}
//Copy assignment
Mystring& Mystring::operator=(const Mystring& rhs) {
std::cout << "Copy assignment called." << std::endl;
if (this == &rhs)
return *this;
delete[] str;
str = new char[std::strlen(rhs.str) + 1];
std::strcpy(str, rhs.str);
return *this;
}
//Move assignment
Mystring& Mystring::operator=(Mystring&& rhs) {
std::cout << "Move assignment called." << std::endl;
if (this == &rhs)
return *this;
delete[] str;
str = rhs.str;
rhs.str = nullptr;
return *this;
}
So, the problem is that I don't understand the process of this program. I took the debugger and everything is fine:
For the first object that I want to add, the "one args constructor" is called first so I create my object, then "the move constructor" gets called so I steal the data and null the pointer of the original object. I finish it, push my object into the vector and I destroy the original object that now sits with a null pointer in it.
Here I get lost for good, because the process looks the same except after that line std::cout << "Move constructor called." << std::endl;
the control goes to the copy constructor and I notice (I hope I'm not wrong) that he does a copy of Larry
.
The problem is: I have no clue why the compiler wants to make a copy of Larry
. It's there, so the compiler can't simply create Moe
, use the move semantics and just push Moe
at the back of the vector? Why does it have to make a copy?
Plus, a very strange behavior for me is that I see a destructor called after that copy constructor of Larry
because I see it being destroyed. I mean, what is the compiler destroying at this point? The original Larry
so it keeps the copy, or what's exactly happening there?
Can someone explain to me what is exactly happening in that second push_back()
?
Upvotes: 0
Views: 412
Reputation: 44238
Actually if you run your code on ideone:
You would only see 3 move ctors called:
Move constructor called.
Move constructor called.
Move constructor called.
But that depends on particular implementation of std::vector
used in your compiler. Yours seem to reallocate space after first element inserted and before the second is added, so std::vector
has to copy first object to the new place using copy ctor and destroying old one. It could not move that object due to the fact you did not make move ctor noexcept
. Additional details can be found here:
How to enforce move semantics when a vector grows?
To validate that this is reason simply add a line after your vector construction:
vector <Mystring> stooges_vec;
stooges_vec.reserve( 3 );
stooges_vec.push_back("Larry"); //11
stooges_vec.push_back("Moe"); //12
stooges_vec.push_back("Curly"); //13
this will prevent reallocation btw object insertion. And of course to fix your issue completely make your move ctor and assignment operator noexcept
Upvotes: 1