我创建了一个简洁的小函数来删除一个不可数及其内容(插入我最近发现的内存泄漏):
generic<typename T>
void CollectionHelpers::DeleteEnumerable(IEnumerable<T>^% enumerable)
{
if(enumerable != nullptr)
{
for each( T obj in enumerable)
{
delete obj;
}
delete enumerable;
enumerable = nullptr;
}
}
...但由于某些原因,当我使用调试器进行跟踪时,当我从DeleteEnumerable
函数返回时,列表仍然显示为某些内存。
我认为通过传入作为跟踪引用它应该修改我传入的句柄?我在这里错过了什么?
修改:更全面的测试示例......
这是一个稍微更新的处理器:
using namespace GenericCollections;
using namespace System::Collections::Generic;
using namespace System;
generic<typename T>
void CollectionHelpers::DeleteEnumerable(IEnumerable<T>^% enumerable)
{
if(enumerable != nullptr)
{
for each( T obj in enumerable )
{
Console::WriteLine("Disposing of object");
delete obj;
}
Console::WriteLine("Disposing of enumerable");
delete enumerable;
enumerable = nullptr;
}
if( enumerable == nullptr )
{
Console::WriteLine("enumerable tracking reference is nullptr");
}
else
{
Console::WriteLine("enumerable tracking reference is NOT nullptr");
}
}
这是调用处理程序的代码的缩减示例:
using namespace System;
using namespace GenericCollections;
using namespace System::Collections::Generic;
ref class MyClass
{
private:
int* m_myBuf;
public:
MyClass()
{
m_myBuf = new int[100];
Console::WriteLine("MyClass::MyClass()");
}
~MyClass()
{
delete [] m_myBuf;
m_myBuf = NULL;
Console::WriteLine("MyClass::~MyClass()");
}
};
int main(array<System::String ^> ^args)
{
List<MyClass^>^ myList = gcnew List<MyClass^>;
myList->Add(gcnew MyClass());
myList->Add(gcnew MyClass());
myList->Add(gcnew MyClass());
CollectionHelpers::DeleteEnumerable(myList);
if(myList == nullptr)
{
Console::WriteLine("Original list is disposed of");
}
else
{
Console::WriteLine(String::Format("Original list still referenced: {0}", myList));
}
return 0;
}
......这是输出:
MyClass::MyClass()
MyClass::MyClass()
MyClass::MyClass()
Disposing of object
MyClass::~MyClass()
Disposing of object
MyClass::~MyClass()
Disposing of object
MyClass::~MyClass()
Disposing of enumerable
enumerable tracking reference is nullptr
Original list still referenced: System.Collections.Generic.List`1[MyClass]
老实说,我并不担心删除可枚举的内容是否是包含在其中的对象需要被删除。我们在整个地方使用这些列表,而且我们的系统内存不足,因为它们没有得到足够快的清理。
更糟糕的是,我们依靠DeleteEnumerable
函数将列表跟踪引用设置为nullptr,但是当它们被传回时,跟踪引用似乎没有更新。从控制台输出中可以看出,这不仅仅是调试器问题。
我不明白为什么
答案 0 :(得分:2)
好的,这种行为确实很奇怪。这就是实际发生的事情:
您有一个List<MyClass^>^
类型的局部变量,并且您正在调用类型为IEnumerable<T>^%
的参数的方法。这是个问题。该方法可以将其参数设置为array<T>^
。在C#中,不允许这样的代码,但出于某种原因,它在C ++ / CLI中是允许的。它编译成如下:
List<MyClass^>^ myList = …;
IEnumerable<MyClass^>^ enumerable = myList;
CollectionHelpers::DeleteEnumerable(enumerable);
你看到了问题吗?如果您在nullptr
中将引用设置为DeleteEnumerable()
,则局部变量enumerable
会更改,但不会更改myList
。
要验证,请将您的代码更改为:
IEnumerable<MyClass^>^ myEnumerable = myList;
CollectionHelpers::DeleteEnumerable(myEnumerable);
if(myEnumerable == nullptr)
{
Console::WriteLine("Original list is disposed of");
}
else
{
Console::WriteLine(String::Format("Original list still referenced: {0}", myList));
}
并正确打印“原始列表已被处理掉”。
如果你想知道为什么 C ++ / CLI会以这种方式运行,那么跟踪引用的行为是否与普通的C ++引用相似?