当我push_back到向量时调用析构函数

时间:2014-02-15 13:49:11

标签: c++ vector destructor

我有这个类定义:

class FlashStream
{
public:
    explicit FlashStream(const char * url, vector<uint8> * headers, vector<uint8> * data, void * ndata, void * notifyData = NULL, uint32 lastModified = NULL);
    ~FlashStream();
private:        
    NPStream      _stream;
    // ...
}

NPStream description

及其实施:

FlashStream::FlashStream(const char * url, vector<uint8> * headers, vector<uint8> * data, void * ndata, void * notifyData, uint32 lastModified)
{
    // ...
    memset(&_stream, 0, sizeof(NPStream));

    _stream.headers = new char[data->size()]; 

    memcpy((void*)_stream.headers, &(*data)[0], data->size());
    // ...
}

FlashStream::~FlashStream()
{
    // ...
    if(_stream.headers)
        delete [] _stream.headers;
    _stream.headers = NULL;
    // ...
}

现在,当我运行此代码时:

// ...
vector<FlashStream> _streams;
// ...
_streams.push_back(FlashStream(url, headers, data, _npp.ndata, notifyData, lastModified));
// ...

有时我在delete [] _stream.headers;的析构函数中FlashStream出现错误,当我push_back() vector<FlashStream> _streams时会调用此错误。

我读过this question on SO和其他几个,但所有相同的人都不知道如何优雅有效地解决问题。可能是复制构造函数中的问题,但我不知道如何通过NPStream.headersNPStream.url的内存分配来实现它?

4 个答案:

答案 0 :(得分:5)

本声明:

_streams.push_back(FlashStream(url, headers, data, _npp.ndata, notifyData, lastModified));

相当于:

{
    FlashStream temp(url, headers, data, _npp.ndata, notifyData, lastModified);
    _streams.push_back(temp);
    // temp gets destroyed here
}

因此,您创建一个临时的FlashStream对象,该对象被复制到向量中,然后被破坏。您可以通过在C ++ 11中使用emplace_back()来避免这种情况:

_streams.emplace_back(url, headers, data, _npp.ndata, notifyData, lastModified);

答案 1 :(得分:3)

在两种情况下可以调用push_back的析构函数。

如前所述,第一种情况是将临时对象推送到向量时发生。这不是最好的主意,因为将调用构造函数,复制构造函数和析构函数。您可以在向量中存储指向对象的指针,以避免冗余调用。

C ++ 11附带了可能对您有所帮助的新功能。

为避免不必要的复制,请使用std :: vector :: emplace_back(Args&amp;&amp; ... args)

它在向量中就地构造对象而不是复制。

您也可以使用push_back的移动版本(value_type&amp;&amp; val)。只需在您的类中定义移动构造函数,移动版本的push_back将自动为临时对象工作。

调用析构函数时的第二种情况是达到向量的容量。 Vector有两个主要值:大小和容量。大小是当前在向量中保存的元素的数量。容量是以元素类型为单位测量的内部矢量存储的大小。所以,当你推回元素时,你正在增加向量的大小。如果存储大小不足以推送新元素,vector会执行重新分配以增加其容量。重新分配后,向量使用复制构造函数重新构造其对象,并使用析构函数删除旧对象。因此,在psuh_back中,vector可以多次调用对象的析构函数。

要降低puhs_back中矢量重新调整大小的成本,请始终使用std :: vector :: reserve方法为您的对象预分配存储空间。

std::vector<int> vec;
vec.reserve(20);
for(int i = 0; i<20; ++i)
    vec.push_back(i)

您也可以通过定义移动构造函数来降低对象复制的成本。

class C
{
public:
    C(int c)
        : m_c(c)
    {
        std::cout << "C(int c)" << std::endl;
    }
    C(C&& c)
        : m_c(c.m_c)
    {
        std::cout << "C(C&& c)" << std::endl;
    }
    C(const C& c)
        : m_c(c.m_c)
    {
        std::cout << "C(const C& c)" << std::endl;
    }
    ~C()
    {
        std::cout << "~C()" << std::endl;
    }
private:
    int m_c;
};

int main()
{
    std::vector<C> vc;
    for (int i = 0; i < 100; ++i)
        vc.push_back(C(i));
    return 0;
}

如果编译并运行它,您将看到根本没有调用“C(const C&amp; c)”。由于定义了构造函数的移动版本,push_back和reallocate将移动您的对象而不是复制。

答案 2 :(得分:0)

当你向后推时,你正在创建一个临时对象 他就是那个被召唤出来的人。

答案 3 :(得分:0)

在矢量/地图等中放置对象的副本是个坏主意。析构函数在临时对象被销毁时调用。每当Vector / Map调整大小或重新排列时,都会再次调用对象的析构函数。

要避免这些,您应该存储指向这些对象的指针。您可能希望在此处使用shared_ptrs。