Деструктор вызывается для объекта при добавлении его в std :: list - PullRequest
9 голосов
/ 05 февраля 2009

У меня есть объект 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());
}

Ответы [ 5 ]

13 голосов
/ 05 февраля 2009

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

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

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

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

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

4 голосов
/ 05 февраля 2009

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

li.push_back( Foo() )

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

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

2 голосов
/ 05 февраля 2009

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

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
1 голос
/ 05 февраля 2009

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

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

0 голосов
/ 05 февраля 2009

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

#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());
}
...