Erase elements from a vector of pointers and deallocate the dynamic memory previously allocated via new operator?

筅森魡賤 提交于 2020-03-23 12:16:02

问题


I want to show you this very simple example, the purpose is to sort some strings allocated dynamically and clean the duplicates resizing the vector and deallocating useless occupated memory.

#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);
}

Why this kind of stuff didn't work? If I comment the line with delete everything works fine but I have of course memory leaks. Another question: if in the signature of the lambda functions I use string* (instead of the typedef string_ptr) I get nasty compilation errors, why?

Sorry for my bad english, I hope the questions are clear enough.


回答1:


As stated, the std::unique function basically makes those items that are placed on the right-side of the returned iterator zombie elements. They can be accessed, but they're useless. That's why your delete does not work correctly when you applied it to these items.

If your goal is to partition off the unique items, but at the same time keep their validity, the algorithm function you may want to use is std::stable_partition, with a usage of std::set. So in place of std::unique, you can do the following:

#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;  
});

Basically, we use the std::set to store values we initially find. On subsequent calls to the lambda function, we check for duplicates by querying the set::count() function. If it returns 1, then the item already exists in the set, 0 otherwise. So to place the duplicate items to the right of the partition, we need to return false, and brand new items, we return true (and also we add the item to the set if it's a new item). So basically, we've written a non-destructive version of std::unique by using std::stable_partition.

Thus this results in the unique items not only being partitioned off to the right of the returned iterator of std::stable_partition, those items are perfectly valid and can be used for whatever purpose you see fit (in your case, you wanted to delete them).

Note that this works, as shown by this Live Example

Also, you could use std::partition, but this function does not preserve the relative order of the items. You may want to use std::partition instead, but I am assuming you want to keep the order of the elements.




回答2:


The problem is that you're deleting elements which are still pointing to valid string (in your case, one of the unique strings). unique function gives the iterator to the element that is just after last element which is not removed. After calling unique, you are deleting everything from last -> v.end(). This is deleting some string which is in the unique part of the vector. To make things clear here is the output after sorting:

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

And after calling unique:

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

Note that I've modified the print_v function to also print the addresses of the strings. As you can see, the string naba is at a memory location 0xca9d50 and after the last unique element i.e. saba, the duplicate string naba is exactly the same as the one earlier, i.e. is stored at the same address. So when you're calling delete, you're invalidating the first string's address as well. So when you call print_v next it sees that the address is invalid and gives you a segfault.



来源:https://stackoverflow.com/questions/39598883/erase-elements-from-a-vector-of-pointers-and-deallocate-the-dynamic-memory-previ

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!