2016-09-20 22 views
1

我想向你展示這個非常簡單的例子,其目的是對一些動態分配的字符串進行排序,並清理重新調整向量大小並重新分配無用的佔用內存的副本。擦除指針向量中的元素並釋放先前通過new運算符分配的動態內存?

#include <iostream> 
#include <vector> 
#include <string> 
#include <algorithm> 

using namespace std; 

void print_v (vector<string *>& v) 
{ 
    cout << "----" << endl; 
    for (string*& str : v) 
     cout << *str << " "; 
    cout << endl << "----" << endl; 
} 

typedef string * string_ptr; 

int main() 
{ 
    vector<string_ptr> v; 
    v.push_back(new string("aba")); 
    v.push_back(new string("baba")); 
    v.push_back(new string("saba")); 
    v.push_back(new string("aba")); 
    v.push_back(new string("naba")); 
    v.push_back(new string("aba")); 
    v.push_back(new string("saba")); 
    v.push_back(new string("laba")); 

    print_v(v); 

    sort(v.begin(), v.end(), [](const string_ptr &a, const string_ptr &b){ 
     return a->compare(*b) < 0; 
    }); 

    auto last = unique(v.begin(), v.end(), [](const string_ptr &a, const string_ptr &b) { 
     return a->compare(*b) == 0; 
    }); 

    print_v(v); 

    for_each(last, v.end(), [](string_ptr &a){ 
     delete a;  //if I comment this line everything works "fine" 
     a = nullptr; 
    }); 

    v.erase(find(v.begin(), v.end(), nullptr) , v.end()); 

    print_v(v); 
} 

爲什麼這種東西不起作用?如果我用delete評論該行,一切正常,但我當然有內存泄漏。另一個問題:如果在使用lambda函數的簽名中使用string*(而不是typedef string_ptr),我會收到令人討厭的編譯錯誤,爲什麼?

對不起,我的英語不好,我希望問題很清楚。

+3

只要有一個「矢量」就好多了。你需要額外的動態分配?除非你有重要的理由沒有在這裏陳述/展示,否則大部分上述代碼是不必要的。 –

+0

'std :: string'並不是動態使用的,你不應該試圖自己管理這樣的內存問題,因爲'std :: unique_ptr '已經在管理STL集合中對象的銷燬方面做了它的工作。 STL已經提供了2個數據結構來管理唯一對象:'std :: set'和'std :: unordered_set'。 – Jack

+0

如果這只是一個例子,爲什麼不使用unique_ptr ?當元素被擦除時它將刪除內存。然而,請謹慎使用,即首先需要將std :: move放入數組中。 – Robinson

回答

1

如前所述,std::unique函數基本上使放置在返回的迭代器殭屍元素右側的那些項目。他們可以訪問,但他們沒用。這就是爲什麼當您將它應用到這些項目時,delete無法正常工作。

如果您的目標是劃分獨特的項目,但同時保持它們的有效性,您可能想要使用的算法函數是std::stable_partition,使用std::set。因此,在地方std::unique,你可以做到以下幾點:

#include <algorithm> 
#include <set> 
//... 
std::set<std::string> stringset; 
auto last = std::stable_partition(v.begin(), v.end(), [&stringset](const string_ptr& a) 
{ 
    if (stringset.count(*a)) return false; 
    stringset.insert(*a); return true; 
}); 

基本上,我們使用std::set存儲值,我們最初發現。在隨後調用lambda函數時,我們通過查詢set::count()函數來檢查重複項。如果它返回1,則該項目已經存在於該集合中,否則爲0。因此,爲了將重複的項目放在分區的右側,我們需要返回false和全新的項目,我們返回true(並且如果它是新項目,我們也將該項目添加到該集合)。所以基本上,我們通過使用std::stable_partition編寫了std::unique的非破壞性版本。

因此,這會導致獨特的項目不僅被分割到std::stable_partition的返回迭代器的右側,這些項目是完全有效的,可用於任何您認爲合適的目的(在您的情況下,您希望delete他們)。

需要注意的是這個工程,如由本Live Example

此外,您還可以使用std::partition,但這個函數不保留項目的相對順序。您可能需要使用std::partition,但我假設您想要保留元素的順序。

+0

'stable_partition'完全符合我的需求,非常感謝您指出這個功能! – Murray

1

問題是你刪除的元素仍然指向有效的字符串(在你的情況下,唯一的字符串之一)。 unique函數將迭代器賦予緊接在最後一個元素之後的元素,該元素不會被刪除。在調用unique之後,您將刪除last - > v.end()中的所有內容。這是刪除一些在矢量的獨特部分中的字符串。爲了使事情在這裏明確的是排序後的輸出:

aba 0xca9c20 aba 0xca9d20 aba 0xca9cf0 baba 0xca9c70 laba 0xca9e00 naba 0xca9d50 saba 0xca9cc0 saba 0xca9dd0 

,並呼籲unique後:

aba 0xca9c20 baba 0xca9c70 laba 0xca9e00 naba 0xca9d50 saba 0xca9cc0 naba 0xca9d50 saba 0xca9cc0 saba 0xca9dd0 

請注意,我已經修改了print_v功能也打印字符串的地址。正如你所看到的,串NABA是一個內存位置0xca9d50和最後一個獨特的元素,即薩巴,重複的字符串NABA是完全一樣的一個較早的,即存儲在同一個地址之後。所以當你打電話給delete時,你也會使第一個字符串的地址無效。因此,當您在接下來撥打print_v時,它會看到地址無效並且會給您一個段錯誤。

+0

哎喲,我沒有想到這個......現在很清楚我的錯誤。謝謝! – Murray

相關問題