When I use TArray with iterator, and selectively remove some of the elements, editor will crash during execution.
source code:
.h
class TestElem {
public:
int ID;
TestElem(int idx);
~TestElem();
};
TArray<TestElem *> TestTArray2;
.cpp
for (i = 0; i < 10; ++i)
{
TestTArray2.Add(new TestElem(i));
}
for (auto it : TestTArray2)
{
UE_LOG(LogSanguo, Warning, TEXT("index for tarray2 %d"), TestTArray2.Find(it));
if (it->ID == 3)
{
// TestTArray2.RemoveAll([&it](TestElem *& Element) { return Element->ID == it->ID; }); --> cause crash
// TestTArray2.RemoveAt(TestTArray2.Find(it), 1, true); --> cause crash, in the code I comment them all
}
UE_LOG(LogSanguo, Warning, TEXT("remove done for tarray2"));
}
All remove* has issues, takes Remove() as an example, in the array.h, it looks like:
int32 Remove(const ElementType& Item)
{
CheckAddress(&Item);
// Element is non-const to preserve compatibility with existing code with a non-const operator==() member function
return RemoveAll([&Item](ElementType& Element) { return Element == Item; });
}
the checkAddress function here is to “Checks that the specified address is not part of an element within the container.” Certainly it is in the range, and I want to remove it. So it will fail…
I believe removesingle() and remove*() has similar issue…
What is the callstack for the crash that you receive? I’ll need to see that information to know what is causing the issue as it could be multiple causes.
You can check the call_stack.txt for details, briefly:
For RemoveSingle() call it looks like:
achineId:085FE37B45808B58B13A07996A6A627A
EpicAccountId:7f01c5707ae54a0ea3107f4922699b56
Unknown exception - code 00000001 (first/second chance not available)
"Assertion failed: (Index >= 0) &
(Index < ArrayNum) [File:D:\Program
Files\Epic
Games\4.8\Engine\Source\Runtime\Core\Public\Containers\Array.h]
[Line: 678] Array index out of
bounds: 9 from a
I should’ve expected the asserts to get cut off like they did. Could you post the crash logs themselves as it won’t limit itself as the callstack does? They can be found in the project directory under Saved > Logs.
In the meantime, it seems like the issue in RemoveSingle() is that you may be, somewhere in the code, referencing an element of the array that is out of the bounds of the array and doesn’t exist. I’m unsure about the other one so far.
It’s not safe to remove from an array while iterating through it. Remove/Add during iteration can cause the memory to be reallocated, causing a crash. The better way to structure the code is to store off the ID of the item you are looking for and then use that to remove after the loop exits. For instance:
int32 ItemIndex = -1;
for (auto item : Items)
{
if (item.ID == 3)
{
ItemIndex = TestTArray2.Find(item);
break;
}
}
if (Items.IsValidIndex(ItemIndex))
{
Items.RemoveAt(ItemIndex);
}
However, that code is the slower way to do it, since Find also traverses the array doing comparisons. Better code is:
int32 ItemIndex = -1;
for (int32 Index = 0; Index < Items.Num(); Index++)
{
if (Items[Index].ID == 3)
{
ItemIndex = Index;
break;
}
}
if (Items.IsValidIndex(ItemIndex))
{
Items.RemoveAt(ItemIndex);
}