Деструктор вызывает объект при добавлении его в std::list

StackOverflow https://stackoverflow.com/questions/515071

  •  21-08-2019
  •  | 
  •  

Вопрос

У меня есть объект Foo и std::list, содержащий его экземпляры.Моя проблема в том, что когда я добавляю новый экземпляр в список, он сначала вызывает ctor, а затем и dtor.А затем dtor на другом экземпляре (согласно указателю this).

В список добавляется один экземпляр, но поскольку вызывается его dtor (вместе с родительскими объектами), объект нельзя использовать должным образом.

Вот упрощенный код, иллюстрирующий проблему:

#include <iostream>
#include <list>

class Foo
{
public:
    Foo()
    { 
        int breakpoint = 0;
    }
    ~Foo()
    { 
        int breakpoint = 0;
    }
};

int main()
{
    std::list<Foo> li;
    li.push_back(Foo());
}
Это было полезно?

Решение

Когда вы push_back() вашего объекта Foo, объект скопирован к внутренним структурам данных списка, поэтому вызываются Dtor и Ctor другого экземпляра.

Все стандартные типы контейнеров STL в C++ принимают свои элементы по значению, поэтому копируют их по мере необходимости.Например, всякий раз, когда вектору необходимо увеличиться, возможно, что все значения в векторе будут скопированы.

Возможно, вы хотите хранить указатели вместо объектов в списке.При этом вместо объекта копируются только указатели.Но при этом вы должны обязательно удалить объекты, как только закончите:

for (std::list<Foo*>::iterator it = list.begin(); it != list.end(); ++it) {
    delete *it;
}
list.clear();

Альтернативно вы можете попробовать использовать какой-нибудь класс «умного указателя», например из библиотек Boost.

Другие советы

Здесь вы создаете временный Foo:

li.push_back( Foo() )

push_back копирует этот Foo в свои внутренние структуры данных.Временный Foo уничтожается после выполнения push_back, который вызывает деструктор.

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

Используйте этот объект, чтобы понять:

class Foo
{
public:
    Foo(int x): m_x(x)
    { 
    std::cout << "Constructed Object: " << m_x << ")\n";
    }
    Foo(Foo const& c): m_x(c.m_x+100)
    {
    std::cout << "Copied Object: " << m_x << ")\n";
    }
    ~Foo()
    {  
    std::cout << "Destroyed Object: " << m_x << ")\n";
    }
};

Первый главный

std::list<Foo*> li;
li.push_back(Foo(1));

Здесь мы создаем временный объект Foo и вызываем push_back().Временный объект копируется в список, и функция завершает работу.По завершении этого оператора временный объект уничтожается (через деструктор).Когда список уничтожается, он также уничтожает все содержащиеся в нем объекты (Foo — это объект с деструктором, поэтому уничтожение включает вызов деструктора).

Итак, вы должны увидеть что-то вроде этого:

Constructed Object: 1
Constructed Object: 101
DestroyedObject: 1
DestroyedObject: 101

Во втором примере у вас есть:

std::list<Foo*> li;
li.push_back(new Foo(1));

Здесь вы динамически создаете объект в куче.Затем вызовите push_back().Здесь указатель копируется в список (у указателя нет конструктора/деструктора), поэтому больше ничего не происходит.Теперь список содержит указатель на объект в куче.Когда функция возвращает значение, больше ничего не делается.Когда список уничтожается, он уничтожает (обратите внимание на тонкую разницу между уничтожением и удалением) содержащийся в нем объект (указатель), но у указателя нет деструктора, поэтому ничего не произойдет, если произойдет утечка памяти.

Итак, вы должны увидеть что-то вроде этого:

Constructed Object: 1

На самом деле здесь происходит то, что вы сохраняете копировать переданного объекта в списке, поскольку вы отправляете его по значению, а не по ссылке.Таким образом, первый вызываемый dtor на самом деле вызывается для объекта, который вы передаете методу push_back, но к тому времени был создан новый экземпляр, и теперь он хранится в списке.

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

Создание списка, содержащего указатели вместо экземпляров, решает проблему с вызовом деструктора.Но я все еще хочу понять, почему это происходит.

#include <iostream>
#include <list>

class Foo
{
public:
    Foo()
    { 
        int breakpoint = 0;
    }
    ~Foo()
    { 
        int breakpoint = 0;
    }
};

int main()
{
    std::list<Foo*> li;
    li.push_back(new Foo());
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top