std :: emplace_back, вызывающий конструктор других объектов в векторе? - PullRequest
1 голос
/ 07 марта 2020

Я действительно запутался в std::vector::emplace_back. Я запускаю следующий код:

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;
}

и получаю этот вывод:

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

Почему, черт возьми, things.emplace_back("B") вызывает деконструктор вещи под названием A?

Ответы [ 2 ]

3 голосов
/ 07 марта 2020

std::vector сохраняет объекты непрерывно в выделенном блоке памяти. Когда размер этого блока становится слишком маленьким, чтобы добавить в него новые элементы, он должен выделить новый, больший блок памяти и скопировать / переместить существующие в настоящее время элементы вектора в это новое распределение. После этого объекты в предыдущем размещении уничтожаются с использованием вызова деструктора.

Вы не видите, что выполняются копии, потому что вы не определили собственный конструктор копирования. Если вы это сделаете, вы увидите, что копия A - это копия, созданная до уничтожения оригинала A. Если вы определите конструктор перемещения noexcept, вы увидите, что копия A создается до того, как будет уничтожен оригинал A.

1 голос
/ 07 марта 2020

Происходит следующее: когда вы добавляете B к вектору, вектор, вероятно, имеет емкость только для 1 элемента, поэтому ему пришлось переместить свой существующий элемент в другой вектор (уничтожив тот, что находится в исходном хранилище)

Давайте добавим еще несколько журналов, чтобы увидеть, что происходит:

#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;
}

Пример вывода:

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