EnDelt64
EnDelt64

Reputation: 1360

Unintended call of move constructor

I'm practicing C++ move semantics code with above code, but MemoryBlock's move constructor for value 25 is called twice. After that, MemoryBlocks destructor is called twice in a row.

Here's the code:

#include <iostream>
#include <vector>

class MemoryBlock {
private:
    int length;
    int* data;

public:
    // Default constructor
    MemoryBlock() = default;

    // Constructor
    explicit MemoryBlock(int p_length) : length(p_length), data(new int[length]) {
        std::cout << "In MemoryBlock(int), length is " << length << ".\n";
    }

    // Destructor
    ~MemoryBlock() {
        std::cout << "~MemoryBlock() is called. Deleting resources.\n";

        delete[] data;
    }

    // Copy constructor
    MemoryBlock(const MemoryBlock& other) : length(other.length), data(new int[other.length]) {
        std::cout << "In MemoryBlock(const MemoryBlock&), length is " << length << ".\n";

        for (int i = 0; i < length; ++i) {
            data[i] = other.data[i];
        }
    }

    // Move constructor
    MemoryBlock(MemoryBlock&& other) noexcept : length(0), data(nullptr) {
        std::cout << "In MemoryBlock(MemoryBlock&&), length is " << other.length << ".\n";

        length = other.length;
        data = other.data;

        other.length = 0;
        other.data = nullptr; 
    }
};

int main() {
    std::vector<MemoryBlock> vec;

    vec.push_back(MemoryBlock(25)); // (1)
    vec.push_back(MemoryBlock(60)); // (2)

    std::cout << "----------------- End of main() -----------------\n";

    return 0;
}

(try it live)

RESULT:

In MemoryBlock(int), length is 25.            // #1: Generate temporary object
In MemoryBlock(MemoryBlock&&), length is 25.  // Move temporary object #1 to vec's node
~MemoryBlock() is called. Deleting resources. // Destruct temporary object #1
In MemoryBlock(int), length is 60.            // #2: Generate temporary object
In MemoryBlock(MemoryBlock&&), length is 60.  // Move temporary object #2 to vec's node
In MemoryBlock(MemoryBlock&&), length is 25.  // ??
~MemoryBlock() is called. Deleting resources. // ??
~MemoryBlock() is called. Deleting resources. // ??
----------------- End of main() -----------------
~MemoryBlock() is called. Deleting resources. // Destruct vec's node 60
~MemoryBlock() is called. Deleting resources. // Destruct vec's node 25

I want to know why the line marked with ?? is called.

Upvotes: 1

Views: 80

Answers (2)

nayab
nayab

Reputation: 2380

Initially Vector in your code has zero capacity. Its Allocator is allocating memory for one object at push_back.

After vec.push_back(MemoryBlock(60)); // (2) new chunk of memory for 2 elements is allocated and first element is moved to new memory chunk.

use vec.reserve(2); to avoid this.

Upvotes: 3

Michal Špondr
Michal Špondr

Reputation: 1535

It seems std::vector uses some reallocating internally. This is the debugger output just before second "In MemoryBlock(MemoryBlock&&), length is 25." is called.

#0  MemoryBlock::MemoryBlock (this=0x419410, other=...) at memblock.cc:44
#1  0x0000000000401bba in __gnu_cxx::new_allocator<MemoryBlock>::construct<MemoryBlock, MemoryBlock> (this=0x7fffffffd6d0, __p=0x419410, __args#0=...) at /usr/include/c++/9/ext/new_allocator.h:147
#2  0x0000000000401819 in std::allocator_traits<std::allocator<MemoryBlock> >::construct<MemoryBlock, MemoryBlock> (__a=..., __p=0x419410, __args#0=...)
    at /usr/include/c++/9/bits/alloc_traits.h:484
#3  0x0000000000402267 in std::__relocate_object_a<MemoryBlock, MemoryBlock, std::allocator<MemoryBlock> > (__dest=0x419410, __orig=0x4192f0, __alloc=...)
    at /usr/include/c++/9/bits/stl_uninitialized.h:894
#4  0x000000000040216d in std::__relocate_a_1<MemoryBlock*, MemoryBlock*, std::allocator<MemoryBlock> > (__first=0x4192f0, __last=0x419300, __result=0x419410, __alloc=...)
    at /usr/include/c++/9/bits/stl_uninitialized.h:932
#5  0x0000000000402056 in std::__relocate_a<MemoryBlock*, MemoryBlock*, std::allocator<MemoryBlock> > (__first=0x4192f0, __last=0x419300, __result=0x419410, __alloc=...)
    at /usr/include/c++/9/bits/stl_uninitialized.h:946
#6  0x0000000000401ef2 in std::vector<MemoryBlock, std::allocator<MemoryBlock> >::_S_do_relocate (__first=0x4192f0, __last=0x419300, __result=0x419410, __alloc=...)
    at /usr/include/c++/9/bits/stl_vector.h:453
#7  0x0000000000401d73 in std::vector<MemoryBlock, std::allocator<MemoryBlock> >::_S_relocate (__first=0x4192f0, __last=0x419300, __result=0x419410, __alloc=...)
    at /usr/include/c++/9/bits/stl_vector.h:466
#8  0x000000000040195e in std::vector<MemoryBlock, std::allocator<MemoryBlock> >::_M_realloc_insert<MemoryBlock> (this=0x7fffffffd6d0, __position={length = 0, data = 0x101}, __args#0=...)
    at /usr/include/c++/9/bits/vector.tcc:461
#9  0x000000000040176e in std::vector<MemoryBlock, std::allocator<MemoryBlock> >::emplace_back<MemoryBlock> (this=0x7fffffffd6d0, __args#0=...) at /usr/include/c++/9/bits/vector.tcc:121
#10 0x0000000000401606 in std::vector<MemoryBlock, std::allocator<MemoryBlock> >::push_back (this=0x7fffffffd6d0, __x=...) at /usr/include/c++/9/bits/stl_vector.h:1201
#11 0x000000000040128f in main () at memblock.cc:58

Upvotes: 3

Related Questions