似乎每次向向量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());
}
答案 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 != nullptr
,scoped_ptr
和unique_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::resize或vector::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()
是可选的,但在开始构造向量元素之前确保有足够的空间可用。