我遇到很多次代码,其中std :: vector的类成员的std :: vector :: clear()在构造函数和析构函数中被调用。
我不明白为什么需要它:
我错过了什么吗?
答案 0 :(得分:24)
不,你没有遗漏任何东西。我怀疑这是(无害的)voodoo编程,有点像在释放它之后将指针设置为null,或者在GUI代码中随机调用repaint / revalidate。程序员记得它曾经帮助过某种类型的错误,现在不必要地添加它“以防万一”。谁知道,也许它会有所帮助。巫毒。
答案 1 :(得分:19)
从事物的声音来看,编写该代码的人是那些错过了某些东西的人。在ctor或dtor中调用clear()的唯一时间是在其他代码的中间。例如,ctor可能读入一些数据,处理它,然后读入更多数据。在这种情况下,在读取数据时使用单个容器可能会更快,并且每次都清除它,而不是每次迭代都创建一个新容器。
答案 2 :(得分:7)
考虑一下:
#define BOOST_TEST_MODULE StlContainers
#define BOOST_LIB_DIAGNOSTIC
#include <boost/test/unit_test.hpp>
#include <boost/assign.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/assign/std/vector.hpp>
#include <vector>
using namespace boost::assign;
using namespace std;
const vector<int> my_ints_vector = list_of(0)(1)(1)(2)(3)(5)(8)(13)(21)(34);
struct ScopedStruct1
{
ScopedStruct1(const vector<int> & v) : m_v(v) {}
~ScopedStruct1() {}
private :
vector<int> m_v;
};
class A
{
public :
A(int i) : m_i(i) {}
~A() {}
private :
int m_i;
};
struct ScopedStruct2
{
ScopedStruct2() {}
~ScopedStruct2() { for(vector<A*>::iterator it = m_v.begin(); it != m_v.end(); ++it) delete *it; }
vector<A*> m_v;
};
struct ScopedStruct3
{
ScopedStruct3() {}
~ScopedStruct3() { /* no deletion */ }
vector<A*> m_v;
};
BOOST_AUTO_TEST_CASE(StlContainer_storing_something_simple)
{
ScopedStruct1 str(my_ints_vector);
}
BOOST_AUTO_TEST_CASE(StlContainer_storing_pointers_with_delete)
{
ScopedStruct2 str;
for(int i = 0; i < 10; i++)
str.m_v.push_back(new A(i));
}
BOOST_AUTO_TEST_CASE(StlContainer_storing_pointers_without_delete)
{
ScopedStruct3 str;
for(int i = 0; i < 10; i++)
str.m_v.push_back(new A(i));
}
使用boost的unit_test框架我创建了3个测试用例。 unit_test框架是greate,因为它跟踪内存泄漏。 你会注意到第一和第二个测试用例不会产生内存泄漏,但第三种情况是因为不会删除向量的内容。
答案 3 :(得分:5)
不,你是对的。除非在构造函数(或基类的构造函数)中有一些额外的业务需要,但机会很低......
稍后修改
在析构函数的情况下,我看到的一个最常见的错误是有些人认为clear方法也会调用指针向量的删除(向量),当然,情况并非如此
答案 4 :(得分:2)
我能想到的唯一一个有用的地方就是破坏的顺序很重要,析构函数想要确保向量中的对象在其他东西之前被销毁。
当然,最好将代码结构化为不需要;然而,这是一个可以想象的原因。
答案 5 :(得分:2)
尽管到目前为止所说的内容,至少有一种情况是在析构函数中显式调用clear
可能是必要的。
想象一下当被销毁的对象有几个成员子对象时的情况,这些子对象需要一些特定的破坏顺序,即子对象以某种方式相互依赖,并且不正确的破坏顺序将导致不良结果。您可能知道,成员子对象销毁(以及成员初始化)的顺序由类定义中成员声明的顺序决定。因此,实现正确破坏顺序的一种方法是相应地安排成员声明。但是,首先,这不是一个非常好的维护解决方案。其次,所需的破坏顺序可能取决于某些运行时条件。第三,所需的破坏顺序可能与所需的初始化程序相矛盾。这一切都意味着通过重新安排声明来指挥正确的破坏顺序可能是不可能的(或明智的)。
在这种情况下,合理的方法可能是通过调用clean
方法等手动清理一些关键成员子对象,直到销毁顺序依赖“消失”。我想,也许您看到的代码可能是通过在战略性选择的clean
子对象上调用vector
来解决订购问题。
至于在构造函数中调用clean
...我不知道为什么有人会这样做。
答案 6 :(得分:-5)
当然,在解除分配之前,必须在析构函数中调用clear()或resize(0)或等效的说明(std :: _ Destroy_range(...)。
取消分配是通过allocator :: deallocate 完成的,它不运行任何析构函数。它只是释放了记忆。
clear()等效于resize(0),它在分配的缓冲区中的第一个size()事物上运行析构函数
不只是分配了指针,文件句柄,保持的互斥锁,以及对象持有的所有其他可恢复资源。必须运行析构函数。在实例化之前,模板不知道析构函数是微不足道的。如果析构函数是微不足道的,那么它在实例化之后就会被优化