2012-03-14 233 views
2

我创建一个类的列表,它创建的指针的对象一个新的列表:删除指针的指针

FeatureSet::FeatureSet() { 
    this->featuresList = new list<HaarFeature*>; 
    globalCounter = 0; 
} 

现在我想删除列表中的所有对象,然后删除该指针到名单。我的代码是:

FeatureSet::~FeatureSet() { 
    for (list<HaarFeature*>::iterator it = featuresList->begin(); it !=  featuresList->end(); it++) { 
     delete *it; 
    } 
    delete featuresList; // this take a long time (more than half a minute) 
} 

我的问题是什么是最好的方法来解决这个问题?我的方法是否正确?不幸的是,当前最后的操作(删除指向列表的指针)大约需要半分钟。列表有大约250000个对象。

+1

您确定需要存储指向'HaarFeature'的指针吗?它有多大?创建/复制是否昂贵?按价值存储'HaarFeature'实际上可能会加快速度。 – 2012-03-14 19:54:19

+1

另外,你不需要在堆上分配'std :: list'。 'std :: list'已经将对象存储在堆上。最后,你确定'std :: list'比'std :: vector'好吗? – 2012-03-14 19:58:35

+0

我选择了一个列表容器,因为我按顺序遍历所有对象多次。在我看来,矢量清晰度更快,但在创建和按顺序访问对象时速度更慢。我创建了一个指向列表的指针,因为我想确保访问对象,即使FeatureSet对象将被销毁。 HaarFeature类的一个对象很小,但我不确定存储整个对象而不是指向对象的指针会提高清除列表的速度。 – Viper 2012-03-15 10:36:02

回答

0

好吧,所以我进行了一些研究,我测试了四种解决方案。在见下表:(我不能把一个图片,请点击链接)

Comparing an efficiency of list and vector containers

你可以从表中看到,包含对象矢量是最快的解决方案。它也是事实。存储指向对象的指针比包含实际对象要慢得多。

摘要:

  • 对象顺序访问列表汇总是缓慢的,可能是因为它需要“跳跃”,从指针的指针和特定对象没有被存储在连续的存储单元
  • 顺序访问向量很快,可能是因为对象存储在线性连续内存单元中使用迭代器访问连续对象比使用索引要快,这并不奇怪
  • 存储指针而不是r EAL对象是慢得多,特别是容器的分配和解除分配(堆分配)
  • 独立地存储对象类型中,列表中的释放比向量的解除分配

应答慢得多问题通过@Emile Cormier的问 - 为什么我想使用指向容器的指针?这确保了即使在删除父类对象之后也可以访问vector/list元素。请考虑这部分代码:

class A{ 
public: 
A(){ 
    this->test = new vector<int>; 
    for(int i = 0; i < 10; i++){ 
     test->push_back(i); 
    } 
} 
~A(){ 
    cout << "object A is dead\n"; 
    test->clear(); // <--- COMMENTING this line allows to access to the vector's elements "after death" without copying 
} 
vector<int>* GetPointer(){ 
    return this->test; 
} 
private: 
vector<int>* test; 
}; 

class B{ 
public: 
B(){ 
    for(int i = 0; i < 10; i++){ 
     test.push_back(i); 
    } 
} 
~B(){ 
    cout << "object B is dead\n"; 
    test.clear(); 
} 
vector<int> GetbyValue(){ 
    return test; 
} 
private: 
vector<int> test; 
}; 

cout << "\nCASE A\n"; 
A* instance = new A(); 
vector<int>* local = instance->GetPointer(); 
delete instance; //deletion before calling!!! 
//Access is impossible, because clear method in destructor destroys original vector, connected with pointer 
cout << "Printing A\n"; 
for(int i = 0; i < local->size(); i++){ 
    cout << (*local)[i] << "\n"; 
} 
cout << "\nCASE B\n"; 
B* instance2 = new B(); 
vector<int> local2 = instance2->GetbyValue(); 
delete instance2; //deletion before calling!!! 
//Access is still possible, because vector has been copied to local variable 
cout << "Printing B\n"; 
for(int i = 0; i < local2.size(); i++){ 
    cout << (local2)[i] << "\n"; 
} 
cout << "THE END\n"; 

当我不使用指针矢量(情况B),我虽然可以在“B”类对象的删除后,但在现实中我使用了一个访问从矢量对象返回的矢量的本地副本。使用指向矢量的指针(情况A),即使在删除“A”类对象后,我也可以访问矢量对象,当然,我不会在析构函数中手动调用clear()方法。

最后 - 谢谢大家的帮助。我认为我的问题已经解决了。

+1

我很高兴你花时间来测试和测量。当谈到表演时,你不应该依赖直觉。 :) – 2012-03-16 16:59:00

+0

如果你希望你的'vector'在课堂外共享,而不进行复制,目前的最佳做法是使用智能指针。智能指针避免了悬挂指针和内存泄漏的问题。如果你的编译器支持C++ 11,你可以使用'std :: shared_ptr'。如果没有,就有'boost :: shared_ptr'(http://www.boost.org/doc/libs/release/libs/smart_ptr/shared_ptr.htm)。 – 2012-03-16 17:05:51

+0

了解智能指针是一项投资,它至少会支付十倍的回报,因为与指针相关的错误几乎是最难解决的。请参阅http://stackoverflow.com/questions/106508/what-is-a-smart-pointer-and-when-should-i-use-one,http://en.wikipedia.org/wiki/Smart_pointer以了解关于智能指针。 – 2012-03-16 17:09:24

1

您确实需要使用不同的数据结构。根据您的突变模式,您可能会更喜欢容器,如dequevector

1

你的方法是正确的。而且这似乎是要做你想做的事情的恰当方式。但list重新分配是昂贵的。也许尝试一个不同的数据结构?

以下:

for (; _Pnode != _Myhead; _Pnode = _Pnext) 
{ // delete an element 
    _Pnext = _Nextnode(_Pnode); 
    this->_Alnod.destroy(_Pnode); 
    this->_Alnod.deallocate(_Pnode, 1); 
} 

就是代码花费的大部分时间。它需要遍历所有节点并删除它们。 list真的没有办法解决这个问题。

我建议你用一个std::vector,重新分配要快得多。

+0

你说得对。行“删除FeatureSet”调用列表析构函数和.clear()方法。有趣的是,使用迭代器从列表中删除所有对象比删除空列表结构(所有节点)要少很多时间...我会尝试使用不同的容器,但正如我上面所写,我认为使用列表容器对我而言应该会更好。顺便说一句:我听说STL库容器的设计并不理想。 E.g从列表中删除250000个对象(大小相同)只需不到一秒钟。 – Viper 2012-03-15 10:44:45

+0

@Viper,但当垃圾收集器在C#中启动时,开销可能会稍后移动。你*可以做出妥协,在桶中打破你的数据,并有一个列表或类似的向量。 – 2012-03-15 10:51:34

0

鉴于您使用的是一个列表,是的,这是删除子元素的方法。请注意,无论您使用哪个容器,如果您要存储指针,“获取下一个指针并删除它”的操作可能需要相同的时间。测试当然是值得的。

您可能会在删除容器(及其包含的节点)本身时获得一些节省。但是父容器的选择更可能由其他地方的使用决定。举个例子:如果你将小节点添加到小块中,那么矢量将不得不重新分配和复制整个指针块来维护它的内部结构。

这就是说,我也同意上面关于容器本身动态分配的观点。不妨将它作为一个具体的成员变量。 (当然,你仍然需要遍历它并删除节点的子内容,但是这更多的是约定的功能,而不是其他任何东西。