为什么在向向量添加实例时会调用我的类的析构函数?

时间:2012-02-17 16:05:36

标签: c++ stl vector

似乎每次向向量m_test添加对象时,都会调用析构函数方法。我错过了什么吗?我怎样才能防止这种情况发生?

class TEST
{
public:
    TEST();
    ~TEST();
    int * x;
};

TEST::TEST()
{
}

TEST::~TEST()
{
... it is called every time I push_back something to the vector ...
    delete x;
}

    vector<TEST> m_test;
    for (unsigned int i=0; i<5; i++)
    {
        m_test.push_back(TEST());
    }

5 个答案:

答案 0 :(得分:10)

这里的问题是您违反了Rule of Three。你的类有一个析构函数,所以你也需要一个复制构造函数和赋值运算符。或者,您不能允许复制您的课程(例如,将T(T const&)T& operator=(T const&)设为私有,或者从boost::noncopyable派生),然后调整矢量大小而不是使用{{ 1}}。

在第一种情况下,您可以像往常一样push_back上课。在第二个中,语法类似于

push_back

不执行上述任何一项操作都是一个坏主意,因为您很可能在某些时候遇到双重删除问题 - 即使您认为自己永远不会复制或分配std::vector<TEST> vec(5); // vec now has five default-constructed elements of type TEST. TEST 1}},明确禁止它会更安全。

顺便说一下,如果你有成员指针,当对象超出范围时应该删除它们,考虑使用智能指针,如x != nullptrscoped_ptrunique_ptr(也许{ {1}}如果您无法使用Boost或C ++ 11)。

答案 1 :(得分:7)

当您push_back 时,它不会被称为,当临时销毁时,它被称为

要在您的示例中修复它:

TEST test;
for (int i = 0; i < 5; ++i)
{
    m_test.push_back(test);
}

只应该拨打一次。

你的代码在循环中创建了一个临时TEST,在push_back中使用它,然后当循环结束/重复并被销毁时,临时超出范围。这恰好发生,因为临时TEST需要清理。

如果你想避免这种情况,你需要做其他事情,但每次推送都要做一个临时对象。一个可能的解决方案是:

vector<TEST> m_test(5); // Note reserving space in the vector for 5 objects

std::fill(m_test.begin(), m_test.end(), TEST()); // Fill the vector with the default ctor

根据STL的优化方式,这可能不需要制作多份副本。

如果在TEST课程中实施复制构造函数,您也可以获得更好的处理,例如:

TEST::TEST(const TEST & other)
{
    x = new int(*other.x); // Not entirely safe, but the simplest copy ctor for this example.
}

这是否合适,或者你如何处理它,取决于你的类及其需求,但是当你定义了自己的常规构造函数和析构函数时,通常应该有一个复制构造函数(否则编译器将生成一个,并且在在这种情况下,它将导致复制和挂起指针x)。

答案 2 :(得分:1)

vector.push_back()将给定对象复制到其存储区域。您在push_back()调用中构建的临时对象在被复制后会立即销毁,这就是您所看到的内容。有些编译器可能能够优化这个副本,但你的显然不能。

答案 3 :(得分:1)

m_test.push_back(TEST());中,TEST()将创建一个临时变量。向量将其复制到自己的内存后,临时变量被破坏。

您可以这样做:

vector<TEST> m_test(5, TEST());

答案 4 :(得分:0)

为避免破坏临时以避免复制构造函数,请考虑使用vector::resizevector::emplace_back。以下是使用emplace_back的示例:

vector<TEST> m_test;
m_test.reserve(5); 
for ( uint i=0; i<5; i++ ) 
{
    m_test.emplace_back();
}

vector元素将在原地构建而无需复制。当vt被销毁时,每个向量元素都会被自动销毁。

需要c ++ 0x(使用-std=c++0x和gnu)。当然也需要#include <vector>

如果未使用默认构造函数(例如,如果TEST::x是引用而不是指针),只需在emplace_back()的调用中添加参数,如下所示:

class TEST
{
public:
    TEST( int & arg) : x(arg) {;} // no default constructor
    int & x; // reference instead of a pointer.
};

. . . 

int someInt;

vector<TEST> m_test;
m_test.reserve(5);
for ( uint i=0; i<5; i++ ) {
    m_test.emplace_back( someInt ); // TEST constructor args added here.
}

显示的reserve()是可选的,但在开始构造向量元素之前确保有足够的空间可用。