擦除指针向量中的元素并释放先前通过new运算符分配的动态内存?

时间:2016-09-20 16:04:36

标签: c++ memory-management vector

我想向您展示这个非常简单的示例,其目的是对动态分配的一些字符串进行排序,并清理重复大小的向量并释放无用的占用内存。

#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)我会得到令人讨厌的编译错误,为什么?

抱歉我的英语不好,我希望问题很清楚。

2 个答案:

答案 0 :(得分:1)

问题是你要删除仍然指向有效字符串的元素(在你的情况下,是一个唯一的字符串)。 unique函数为迭代器提供了刚刚删除的最后一个元素之后的元素。致电unique后,您将删除最后的所有内容 - &gt;鬻()。这是删除一些在向量的唯一部分中的字符串。这里要清楚的是排序后的输出:

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,在最后一个唯一元素即 saba 之后,重复字符串 naba 与前一个完全相同,即存储在同一地址。因此,当您呼叫delete时,您也会使第一个字符串的地址无效。因此,当您再次致电print_v时,它会发现该地址无效并为您提供了段错误。

答案 1 :(得分:1)

如上所述,#product.rb belongs_to :category #category.rb has_many :products 函数基本上使那些放置在返回的迭代器僵尸元素右侧的项目。他们可以访问,但他们没用。这就是您将rake db:migrate应用于这些项目时无效的原因。

如果您的目标是对特殊项目进行分区,但同时保持其有效性,则您可能希望使用的算法功能为std::stable_partition,使用def change。因此,代替std::unique,您可以执行以下操作:

delete

基本上,我们使用std::set来存储我们最初找到的值。在后续调用lambda函数时,我们通过查询std::unique函数来检查重复项。如果它返回1,则该项已存在于集合中,否则为0。因此,要将重复的项目放在分区的右侧,我们需要返回#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(如果它是新商品,我们也会将商品添加到商品中)。基本上,我们使用set::count()编写了false的非破坏性版本。

因此,这会导致独特的项目不仅被分割到true的返回迭代器的右侧,这些项目完全有效,可以用于您认为合适的任何目的(在您的情况下,您想要std::unique他们。)

请注意,此功能正常,如Live Example

所示

此外,您可以使用std::stable_partition,但此功能不会保留项目的相对顺序。您可能希望使用std::stable_partition,但我假设您要保留元素的顺序。