在将对象添加到std :: list时,析构函数调用了对象

时间:2009-02-05 08:46:59

标签: c++ constructor stdlist

我有一个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 个答案:

答案 0 :(得分:13)

当你对Foo对象进行push_back()时,对象被复制到列表的内部数据结构,因此调用另一个实例的Dtor和Ctor。

C ++中的所有标准STL容器类型都按值获取它们的项目,因此根据需要复制它们。例如,每当矢量需要增长时,矢量中的所有值都可能被复制。

也许你想存储指针而不是列表中的对象。通过这样做,只有指针被复制而不是对象。但是,通过这样做,您必须确保在完成后删除对象:

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

或者,您可以尝试使用某种“智能指针”类,例如来自Boost库。

答案 1 :(得分:4)

你在这里创建一个临时的Foo:

li.push_back( Foo() )

push_back将Foo复制到其内部数据结构中。执行push_back后,临时Foo将被销毁,这将调用析构函数。

你需要一个合适的拷贝构造函数来增加你不想早期破坏的类成员的引用计数 - 或者让它成为私有来强迫你自己使用指针解决方案。

答案 2 :(得分:2)

使用此对象来理解:

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()。临时对象被复制到列表中,函数返回。完成此语句后,临时对象将被销毁(通过析构函数)。当列表被销毁时,它也会销毁它包含的所有obejct(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

答案 3 :(得分:1)

这里实际发生的是您在列表中存储传递对象的副本,因为您是按值而不是按引用发送它。因此调用的第一个dtor实际上是调用传递给push_back方法的对象,但是当时创建了一个新实例,它现在存储在列表中。

如果您不想创建 Foo 对象的副本,请将指针存储在列表中的 Foo 对象而不是对象本身。当然,在执行此操作时,您必须在销毁列表时正确释放内存。

答案 4 :(得分:0)

使列表保持指针而不是实例解决了被调用析构函数的问题。但我仍然想了解它为什么会发生。

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