从指针向量中删除元素,并释放先前通过new操作符分配的动态内存

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

本文关键字:new 操作符 分配 内存 动态 向量 指针 删除 元素 释放      更新时间:2023-10-16

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

#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*(而不是类型定义string_ptr),我会得到讨厌的编译错误,为什么?

对不起,我的英语不好,我希望问题足够清楚。

如上所述,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它们)。

请注意,这是有效的,如下面的示例

所示

也可以使用std::partition,但该函数不保留项的相对顺序。您可能想使用std::partition代替,但我假设您想保持元素的顺序。

问题是您正在删除仍然指向有效字符串的元素(在您的情况下,是唯一字符串之一)。unique函数将迭代器指向最后一个未移除的元素之后的元素。调用unique之后,您将删除last -> v.p 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,并且在最后一个唯一元素saba之后,重复字符串naba与前面的字符串完全相同,即存储在相同的地址。所以当你调用delete时,你也使第一个字符串的地址无效。因此,当您接下来调用print_v时,它会看到地址无效并给您一个段错误。