Почему деструктор вызывается при создании объекта в методе std :: vector :: push_back (T object)? - PullRequest
1 голос
/ 21 февраля 2020

У меня есть эта строка здесь:

someSTLVector.push_back(SomeClass(...));

Я надеялся, что SomeClass будет построен и перемещен в конец вектора, без каких-либо копий. Однако деструктора вызвали сюда. Я попытался изменить это с помощью std::move:

someSTLVector.push_back(std::move(SomeClass(...)));

, но результат не изменился.

Я также попытался определить следующее в SomeClass:

SomeClass(SomeClass&&) = default;
SomeClass& operator= (SomeClass&&) = default;
SomeClass(const SomeClass&) = delete;
SomeClass& operator= (const SomeClass&) = delete;

Это тоже не помогло, деструктора все еще звали. Обратите внимание, что SomeClass содержит ссылку в качестве члена

Очевидно, что SomeClass создается, а затем копируется в вектор. Я не хочу этого избегать, и он должен быть построен как часть вектора (или, по крайней мере, перемещен в вектор, избегая любого копирования). SomeClass управляет ресурсами, которые высвобождаются в деструкторе. Если при копировании объекта вызывается деструктор, ресурс освобождается и объект становится недействительным, указывая на ресурс, который больше не существует.

Как создать экземпляр класса, в котором полученный объект будет помещен в задняя часть вектора, но не копируется (и, следовательно, уничтожается) в процессе?

Ответы [ 2 ]

4 голосов
/ 21 февраля 2020

Я надеялся, что SomeClass будет построен и перемещен в конец вектора, без каких-либо копий.

Вот что происходит.

Однако деструктор был вызван сюда.

Действительно. Это будет временный объект, который вы передали конструктору перемещения:

someSTLVector.push_back(SomeClass(...));
                        ^^^^^^^^^^^^^^

Что есть синтаксис для инициализации временного объекта.

Это Очевидно, что SomeClass создается и затем копируется в вектор.

Ну, переместился , если быть точным. Хотя перемещение - это форма копирования.

Я не хочу этого избегать, и пусть оно будет построено как часть вектора (или, по крайней мере, перемещено в вектор, избегая любого копирования).

Вам уже удалось избежать копирования. Чтобы избежать перемещения, вы можете использовать функцию-член emplace_back вместо push_back:

someSTLVector.emplace_back(...);

. Это перенаправляет аргументы непосредственно в конструктор элемента.


Если при копировании объекта вызывается деструктор, ресурс освобождается и объект становится недействительным, указывая на ресурс, которого больше нет.

Если ваш деструктор освобождает некоторые ресурсы, то конструктор / назначение перемещения по умолчанию, вероятно, не делает то, что вы хотели бы, чтобы они делали. См. Правило пяти / трех.

2 голосов
/ 21 февраля 2020

Перемещаемый объект все еще разрушен. Таким образом, ваш SomeClass, вероятно, перемещен в вектор (вы можете добавить сообщение std::cout << в конструкторе перемещения для проверки), но затем он также будет уничтожен.

Вы можете вызвать std::vector::emplace_back, чтобы получить элемент построен прямо в вектор. Тем не менее, это еще не гарантирует, что ваш объект не будет перемещен (например, если вектор должен расти, он может выделить больше места, а затем переместить все объекты в новое хранилище и затем уничтожить их в исходном местоположении).

Если у вас есть какой-то ресурс, который вы высвобождаете в деструкторе, вам нужно убедиться, что вы не делаете этот релиз, если объект был перемещен. Обычно перемещают конструкторы «пусто» из перемещенного объекта (например, учитывая SomeClass(SomeClass&& other) в этом методе, вы изменили бы other, чтобы «очистить его»). Тогда ваш деструктор сможет увидеть, «пустой» ли он (был перемещен) и не освободить ресурсы, которые у вас есть.

...