2012-02-16 40 views
1

我創建了一個整潔的小功能,刪除IEnumerable和它的內容(堵塞內存泄漏我最近發現):爲什麼我的跟蹤參考不更新?

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

在第一種情況下,使用'list'而不是'enumerable'只是在代碼中出現錯字或實際錯誤? – svick 2012-02-16 16:31:18

+0

錯字在把一些嚴重命名變量 - 現在固定;-) – 2012-02-16 16:33:15

+0

你能不能告訴我們,調用該函數的代碼?換句話說,告訴我們你期望什麼是'nullptr',但不是。 – svick 2012-02-16 16:39:35

回答

2

好吧,這種行爲確實很奇怪。這是實際發生的事情:

您有一個局部變量,類型爲List<MyClass^>^,您正在調用一個參數類型爲IEnumerable<T>^%的方法。這是個問題。該方法可以將其參數設置爲array<T>^。在C#中,這樣的代碼將不被允許,但由於某種原因,它允許在C++/CLI中使用。它編譯成類似於:

List<MyClass^>^ myList = …; 
IEnumerable<MyClass^>^ enumerable = myList; 
CollectionHelpers::DeleteEnumerable(enumerable); 

您是否看到問題?如果您在DeleteEnumerable()中將參考號設置爲nullptr,則本地變量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++引用?

+0

謝謝。我已經改變了'DeleteEnumarable'函數接受一個列表和工作。 – 2012-02-20 08:42:54