Почему моя ссылка отслеживания не обновляется? - PullRequest
1 голос
/ 16 февраля 2012

Я создал аккуратную маленькую функцию для удаления неисчислимого и его содержимого (чтобы устранить утечку памяти, которую я недавно обнаружил):

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, но когда они возвращаются обратно, ссылка отслеживания, похоже, не обновляется. Это не просто проблема отладчика, как вы можете видеть из вывода консоли.

Я не понимаю, почему

1 Ответ

2 голосов
/ 19 февраля 2012

Хорошо, это поведение действительно странно. И вот что на самом деле происходит:

У вас есть локальная переменная типа 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 ++?

...