简体   繁体   中英

std::emplace_back invoking constructor of other objects in vector?

I am really confused about std::vector::emplace_back . I run the following code:

struct Thing {
    explicit Thing(std::string name) : name_{std::move(name)} {
        std::cout << "Constructed a thing called " << name_ << std::endl;
    }

    ~Thing() {
        std::cout << "Deconstructed a thing called " << name_ << std::endl;
    };
    std::string name_;
};


int main() {
    std::vector<Thing> things{Thing("A")};
    std::cout << "Size: " << things.size() << std::endl;
    things.emplace_back("B");
    std::cout << "Size: " << things.size() << std::endl;
}

and get this output:

Constructed a thing called A
Deconstructed a thing called A
Size: 1
Constructed a thing called B
Deconstructed a thing called A
Size: 2
Deconstructed a thing called A
Deconstructed a thing called B

Why on earth does things.emplace_back("B") invoke the deconstructor of the thing called A?

std::vector stores the objects continuously in an allocated memory block. When that block's size becomes too small to add new elements into it, it must allocate a new, larger, memory block and copy/move the currently present elements in the vector into that new allocation. After that, the objects in the previous allocation are destroyed, involving the destructor call.

You are not seeing the copies being made, because you didn't define a custom copy constructor. If you did, you would see that a copy of A is copy constructed before the original A is destructed. If you define a noexcept move constructor, you will see that a copy of A is move constructed before the original A is destructed.

What's happening is that when you add B to the vector, the vector probably only had capacity for 1 element, so had to move it's existing element into another vector (destructing the one in the original storage)

Let's add some more logging to see what happens:

#include <iostream>
#include <string>
#include <vector>

struct Thing {
    explicit Thing(std::string name) : name_{std::move(name)} {
        std::cout << "Constructed a thing called " << name_ << " at " << this << std::endl;
    }
    Thing(Thing&& t) noexcept : name_{std::move(t.name_)} {
        std::cout << "Moved a thing called " << name_ << " from " << &t << " to " << this << std::endl;
    }
    Thing(const Thing& t) : name_{t.name_} {
        std::cout << "Copied a thing called " << name_ << " from " << &t << " to " << this << std::endl;
    }

    ~Thing() {
        std::cout << "Deconstructed a thing called " << name_ << " at " << this << std::endl;
    };
    std::string name_;
};


int main() {
    std::vector<Thing> things{Thing("A")};
    std::cout << "Size: " << things.size() << " Capacity: " << things.capacity() << std::endl;
    things.emplace_back("B");
    std::cout << "Size: " << things.size() << " Capacity: " << things.capacity() << std::endl;
}

Example output:

Constructed a thing called A at 0x1111
Copied a thing called A from 0x1111 to 0x2222
Deconstructed a thing called A at 0x1111
Size: 1 Capacity: 1
Constructed a thing called B at 0x3333
Moved a thing called A from 0x2222 to 0x4444
Deconstructed a thing called A at 0x2222
Size: 2 Capacity: 2
Deconstructed a thing called A at 0x4444
Deconstructed a thing called B at 0x3333

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM