9

我有一个 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());
}
4

5 回答 5

14

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

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

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

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

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

于 2009-02-05T08:53:24.963 回答
4

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

li.push_back( Foo() )

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

您将需要一个适当的复制构造函数来增加您不想提前销毁的类成员的一些引用计数 - 或者将其设为私有以强制自己使用指针解决方案。

于 2009-02-05T08:55:00.280 回答
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()。临时对象被复制到列表中并且函数返回。在此语句完成后,临时对象将被销毁(通过析构函数)。当列表被销毁时,它也会销毁它包含的所有对象(Foo 是一个带有析构函数的对象,因此销毁包括调用析构函数)。

所以你应该看到这样的东西:

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

在第二个示例中,您有:

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

在这里,您在堆上动态创建一个对象。然后调用 push_back()。这里指针被复制到列表中(指针没有构造函数/析构函数),所以没有其他任何事情发生。该列表现在包含指向堆上对象的指针。当函数返回时,什么都不做。当列表被销毁时,它会销毁(注意destroy和delete之间的细微差别)它包含的对象(一个指针)但是一个指针没有析构函数,所以没有任何事情发生,你会泄漏内存。

所以你应该看到这样的东西:

Constructed Object: 1
于 2009-02-05T18:29:37.670 回答
1

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

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

于 2009-02-05T08:58:34.297 回答
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());
}
于 2009-02-05T08:53:53.900 回答