Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why wasn't the move constructor called? [duplicate]

I'm doing an exercise from C++ Primer 5th Edition, which goes like:

Exercise 13.50: Put print statements in the move operations in your String class and rerun the program from exercise 13.48 in § 13.6.1 (p. 534) that used a vector to see when the copies are avoided.(P.544)

The String is a class for practise which behaves like a std::string without using any template. The String.h file:

class String
{
public:
    //! default constructor
    String();

    //! constructor taking C-style string i.e. a char array terminated with'\0'.
    explicit String(const char * const c);

    //! copy constructor
    explicit String(const String& s);

    //! move constructor    
    String(String&& s) noexcept;

    //! operator =
    String& operator = (const String& rhs);

    //! move operator =     
    String& operator = (String&& rhs) noexcept;

    //! destructor
    ~String();

    //! members
    char* begin() const  { return elements;   }
    char* end()   const  { return first_free; }

    std::size_t size()     const {return first_free - elements;  }
    std::size_t capacity() const {return cap - elements;         }

private:

    //! data members
    char* elements;
    char* first_free;
    char* cap;

    std::allocator<char> alloc;

    //! utillities for big 3
    void free();

};

Implementation for default, copy and move constructor from String.cpp :

//! default constructor
String::String():
    elements    (nullptr),
    first_free  (nullptr),
    cap         (nullptr)
{}

//! copy constructor
String::String(const String &s)
{
    char* newData = alloc.allocate(s.size());
    std::uninitialized_copy(s.begin(), s.end(), newData);

    elements = newData;
    cap = first_free = newData + s.size();

    std::cout << "Copy constructing......\n";
}

//! move constructor    
String::String(String &&s) noexcept :
    elements(s.elements), first_free(s.first_free), cap(s.cap)
{
    s.elements = s.first_free = s.cap = nullptr;
    std::cout << "Move constructing......\n";
}

Main.cpp:

int main()
{
    std::vector<String> v;
    String s;
    for (unsigned i = 0; i != 4; ++i)
        v.push_back(s);

    return 0;
}

The output:

Copy constructing......
Copy constructing......
Copy constructing......
Copy constructing......
Copy constructing......
Copy constructing......
Copy constructing......

As can be seen, the move constructor was not called at all. Why wasn't the move constructor called when the vector allocating more memory?

Update:

The info of compiler:

gcc version 4.7.3 (Ubuntu/Linaro 4.7.3-1ubuntu1) 

main.cpp with printing capacity and its ouput:

int main()
{
    std::vector<String> v;
    String s;
    for (unsigned i = 0; i != 4; ++i)
    {
        std::cout << v.capacity() << "\n";
        v.push_back(s);
    }

    return 0;
}

Output:

0
Copy constructing......
1
Copy constructing......
Copy constructing......
2
Copy constructing......
Copy constructing......
Copy constructing......
4
Copy constructing......
like image 245
Yue Wang Avatar asked Jan 07 '14 09:01

Yue Wang


People also ask

What is the difference between a move constructor and a copy constructor?

Move constructor moves the resources in the heap, i.e., unlike copy constructors which copy the data of the existing object and assigning it to the new object move constructor just makes the pointer of the declared object to point to the data of temporary object and nulls out the pointer of the temporary objects.

Is move constructor faster than copy constructor?

It's faster because moving allows the source to be left in a invalid state, so you can steal it's resources. For example, if a object holds a pointer to a large block of allocated memory, a move can simply steal the pointer while a copy must allocate its own memory and copy the whole memory block.

Is move constructor automatically generated?

If a copy constructor, copy-assignment operator, move constructor, move-assignment operator, or destructor is explicitly declared, then: No move constructor is automatically generated. No move-assignment operator is automatically generated.

What are the three times the copy constructor is called automatically?

In C++, a Copy Constructor may be called for the following cases: 1) When an object of the class is returned by value. 2) When an object of the class is passed (to a function) by value as an argument. 3) When an object is constructed based on another object of the same class.


1 Answers

I reproduce with gcc 4.7.1 on MinGW...

Adding ~String() noexcept solves the issue...

like image 175
Jarod42 Avatar answered Sep 18 '22 12:09

Jarod42