从指针数组中移除元素 - C++

3

我有一个结构体指针数组,它看起来像这样:

index                 [0   1   2   3   4]
value of structure     21  7   42  30  NULL

我试图删除索引2(42)处存储的值。我认为由于该数组的每个元素都是指向结构体的指针,所以为了删除42,我需要首先在 arr[2] 上调用 delete,然后将 arr[2] 设置为 arr[3],再在 arr[3] 上调用 delete,然后将 arr[3] 设置为 arr[4]。但这并没有起作用,所以我决定尝试不使用 delete 关键字,只需执行 arr[2] = arr[3] 和 arr[3] = arr[4],结果它奏效了。 那么我的问题是,为什么我不必使用 delete 关键字就能够这样做呢?我想如果我只是将 arr[2] 设置为 arr[3],那么由 arr[2] 指向的结构体将会丢失,我将得到一个内存泄漏。难道不是这种情况吗?

使用 std::vector 替代。删除您想要删除的指针,然后从向量中 erase 它。 - Some programmer dude
实际上,我正在尝试学习学校的一个概念,所以我需要这样做。 - this is a long display name
请记得回来接受您认为最有帮助的答案。这将使您、发帖人和整个社区受益。 - code_dredd
5个回答

6
我认为由于这个数组的每个元素都是一个指向结构体的指针,所以要删除42,首先需要调用delete arr[2]。
是的,您需要在arr [2]上使用delete以释放arr [2]指向的内存。
然后我会说arr [2] = arr [3]。
到目前为止还不错。
然后在arr [3]上删除。 这就是问题所在。 假设您有以下代码:
int arr_cnt = 5;
int *arr[arr_cnt];
for(int i = 0; i < arr_cnt; ++i)
    arr[i] = new int(i+arr_cnt);  // using ints for simplicity, but concept is the same

您的数组arr现在看起来像这样:
idx   *arr[]     values in heap, due to using 'new' operator
    +-----+
0   | a---|----> 5
    +-----+
1   | b---|----> 6
    +-----+
2   | c---|----> 7
    +-----+
3   | d---|----> 8
    +-----+
4   | e---|----> 9
    +-----+

其中的字母代表new所返回的不同内存地址。

这意味着,为了正确地删除索引为2的元素,您需要:

  1. 使用delete删除arr[2]以避免内存泄漏,
  2. 覆盖arr[2],并用某个其他仍然有效的地址替换它(在此步骤之前使用arr[2]会触发一个seg-fault)
  3. 将复制到arr[2]的数组位置设置为null (如下所示)
  4. 减少您数组的长度(例如:arr_cnt--;)

换句话说:

delete arr[2];      // step 1, address 'c' no longer valid
arr[2] = arr[4];    // step 2, arr[2] is now 'e', which points to 9 just like arr[4]
arr[4] = NULL;      // step 3, arr[4] is now invalid
--arr_cnt;          // step 4

图表现在应该是这样的:
idx   *arr[]     values in heap, due to using 'new' operator
    +-----+
0   | a---|----> 5
    +-----+
1   | b---|----> 6
    +-----+
2   | e---|-----------+   // address 'e' used to be in arr[4]
    +-----+           |
3   | d---|----> 8    |
    +-----+           |
4   | nil |      9 <--+
    +-----+

那么arr[3] = arr[4]是无效的

如果您按照这个图示进行操作,可能已经注意到使用delete关键字会使两个条目都失效。换句话说,如果我们跳过第二张图并尝试使用您提出的delete arr[2]; arr[2] = arr[3]; delete arr[3]逻辑,您最终会得到:

delete arr[2];
    +-----+
0   | a---|----> 5
    +-----+
1   | b---|----> 6
    +-----+
2   | c---|----> ?  // invalidated
    +-----+
3   | d---|----> 8
    +-----+
4   | e---|----> 9
    +-----+

arr[2] = arr[3];
    +-----+
0   | a---|----> 5
    +-----+
1   | b---|----> 6
    +-----+
2   | d---|-+
    +-----+ |
3   | d---|-+--> 8  // both have the same address, so point to the same place
    +-----+
4   | e---|----> 9
    +-----+

delete arr[3];
    +-----+
0   | a---|----> 5
    +-----+
1   | b---|----> 6
    +-----+
2   | d---|-+
    +-----+ |
3   | d---|-+--> ?  // both invalid now, but not set to null
    +-----+
4   | e---|----> 9
    +-----+

因此,我决定尝试不使用delete关键字,而是执行arr[2]=arr[3]和arr[3]=arr[4],这样就可以了。但现在你存在内存泄漏的问题。在C++中,您必须像在C中一样始终要delete每个new。
那么我的问题是,为什么我不需要使用delete关键字来完成这个操作?您确实需要使用它。问题在于,您无意中使更多内容失效,并尝试使用已被释放的内存。当程序尝试访问这样的内存时,会导致段错误。我不记得Windows会显示什么准确消息,但可能是某种未处理的异常消息。(Segmentation fault倾向于是GNU / Linux术语)。
我认为如果我只是将arr[2]设置为arr[3],那么被arr[2]指向的结构体将丢失,我会出现内存泄漏。不是这样吗?您在这里是正确的。问题不在于您对new / delete关系的理解,而在于您所描述的赋值是浅复制,因此您删除了更多内容。

我认为你在这些步骤上有误。{delete arr[2]; => arr[2] = arr[4]; => arr[4] = NULL; => --arr_cnt; }第一步会导致第二步出现段错误。改变我的想法。 - Rafid
1
@Rafid 它不会导致段错误;我的代码是正确的。此外,arr[2]是程序自己内存空间中的地址,因此在delete之后访问它更可能返回垃圾而不是段错误。请参见什么是段错误?了解更多信息。(在答案下面关于悬挂指针的评论中,请注意,即使有真正的错误,你也不总是会得到段错误。)要有可能出现段错误,我必须解引用指针,特别是如果它被设置为nullptr,但我从来没有这样做。无论你是否改变了主意都无关紧要。 - code_dredd

0

检查一下,确保您真的有一个结构指针数组。另外,如果您在索引2处删除元素后确实有一个结构指针数组,请不要在3或之后的索引上调用delete。只需将arr [2] = arr [3]; arr [3] = arr [4];即可。这不是内存泄漏,因为您只是复制了指向结构体的指针,而不是实际的结构体。

struct A
    {
    };


//This is an array of A structures
A * array1;

//This is an array of pointers to A structures
A** array2;

0
我需要先调用delete arr[2],然后说arr[2] = arr[3],再删除arr[3],然后arr[3] = arr[4]。
你只需要简单地删除arr[2]并将所有项向左移动,而不删除它们。如果您对所有连续对象应用delete,则会失去它们所有。
当然,您可以简单地移动数组而不删除arr[2],但这会导致内存泄漏。该对象将不会被处理。

0
你的问题在于你删除了不应该删除的指针。
在你删除了索引2处的旧指针并将索引3处的指针赋值给了索引2之后,你有两个指向同一对象的指针:在索引2和3处。删除由索引3指向的对象会删除这两个指针所指向的对象,使得索引2处的指针无效。
解决你的问题的方法(除了切换到我推荐的std::vector之外)是删除索引2处的第一个对象,将较高索引处的值下移(例如,array[2] = array[3]),并将最后一个指针设置为nullptr。不需要再进行删除操作。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接