user13261243
user13261243

Reputation:

Vector push_back method working with objects and move semantics

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:

  1. 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.

  2. 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

Answers (1)

Slava
Slava

Reputation: 44238

Actually if you run your code on ideone:

Live example

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

Related Questions