我有这个类定义:
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;
// ...
}
及其实施:
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.headers
和NPStream.url
的内存分配来实现它?
答案 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。