2012-03-14 173 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

鑑於您使用的是一個列表,是的,這是刪除子元素的方法。請注意,無論您使用哪個容器,如果您要存儲指針,「獲取下一個指針並刪除它」的操作可能需要相同的時間。測試當然是值得的。

您可能會在刪除容器(及其包含的節點)本身時獲得一些節省。但是父容器的選擇更可能由其他地方的使用決定。舉個例子:如果你將小節點添加到小塊中,那麼矢量將不得不重新分配和複製整個指針塊來維護它的內部結構。

這就是說,我也同意上面關於容器本身動態分配的觀點。不妨將它作爲一個具體的成員變量。 (當然,你仍然需要遍歷它並刪除節點的子內容,但是這更多的是約定的功能,而不是其他任何東西。