2010-10-01 2 views
1

Я написал приложение с использованием wxWidgets, которое использует wxList. У меня есть случайные судороги (segfault) в деструкторах, которые собирают данные списка. Я не смог найти определенный способ удаления элементов из списка (Erase() VS DeleteNode()). Даже итерация над элементами имеет два варианта (list-> GetFirst() VS list-> begin()).Каков правильный способ обработки связанных списков в wxWidgets?

Ниже приведен тестовый класс, показывающий подход, который я использую в своем приложении. Тест проходит отлично, без сбоев. Кажется, что некоторые указатели используются после того, как они освобождены, но я не могу сказать это, посмотрев на код. Я полагаю, что я делаю что-то неправильно в вызовах Erase() и DeleteContents().

P.S: в приложении список содержит около 15 тысяч наименований, а не только 9 в тесте.

#include <wx/list.h> 
#include <wx/log.h> 

class TestItem 
{ 
public: 
    TestItem(int _x, int _y) { x = _x; y = _y; } 
    int x; 
    int y; 
}; 

WX_DECLARE_LIST(TestItem, TestList); 

#include <wx/listimpl.cpp> 
WX_DEFINE_LIST(TestList); 

class Test { 

public: 
    TestList *list; 
    Test() { 
     list = new TestList; 
    } 

    ~Test() { 
     Clean(); 
     delete list; 
    } 


    void CreateAndAddToList(int x, int y) { 
     TestItem *item = new TestItem(x, y); 
     list->Append(item); 
    } 

    void PrintAll() { 
     wxLogMessage(wxT("List size: %d"), list->GetCount()); 
     wxTestListNode *node = list->GetFirst(); 
     while (node) { 
      TestItem *item = node->GetData(); 
      wxLogMessage(wxT("Item: %d, %d"), item->x, item->y); 
      node = node->GetNext(); 
     } 
    } 

    void DeleteAllX(int x) { 
     wxTestListNode *node = list->GetFirst(); 
     while (node) { 
      TestItem *item = node->GetData(); 
      if (item->x != x) { 
       node = node->GetNext(); 
       continue; 
      } 
      wxTestListNode *toDelete = node; 
      node = node->GetNext(); 
      wxLogMessage(wxT("Deleting item: %d, %d"), item->x, item->y); 
      list->Erase(toDelete); 
      delete item; 
     } 
    } 

    void Clean() { 
     list->DeleteContents(true); 
     list->Clear(); 
    } 

    static void DoAllTests() { 
     Test *t = new Test; 
     t->CreateAndAddToList(1, 1); 
     t->CreateAndAddToList(1, 2); 
     t->CreateAndAddToList(1, 3); 
     t->CreateAndAddToList(2, 1); 
     t->CreateAndAddToList(2, 2); 
     t->CreateAndAddToList(2, 3); 
     t->CreateAndAddToList(3, 1); 
     t->CreateAndAddToList(3, 2); 
     t->CreateAndAddToList(3, 3); 
     t->PrintAll(); 
     t->DeleteAllX(2); 
     t->PrintAll(); 
     t->Clean(); 
     t->PrintAll(); 
     delete t; 
    } 
}; 
+1

Вы можете избежать головных болей и проблем и использовать 'std :: list'. Уже закодированы и протестированы миллионами пользователей. –

+0

В документах я не заметил, что wxList устарел. В любом случае, новый API практически идентичен std :: list. Я изменил свое приложение, чтобы использовать std :: list, некоторые из ошибок, похоже, исчезли. По крайней мере, сейчас у меня есть гораздо больше документации о бэкэнде списка. – streeto

ответ

0

О различии между list->GetFirst() и list->begin() в wxList API, это, кажется, что list->GetFirst() возвращает NULL, если список пуст, и list->begin() возвращает значение итератора до конца list->end(), как обычно, для других итераторов. list->GetFirst() - это старый API, list->begin() - новый. Основное преимущество заключается в том, что вы можете использовать шаблоны, ожидающие итератора, с помощью wxList.

wxList считается устаревшим и заменяется на std :: list, но это не должно вас слишком беспокоить, так как оно сделано внутренне с новыми версиями wx (wxList просто становится тонкой оболочкой поверх wxList).

То, как вы его используете, кажется, все равно, и я не вижу очевидной ошибки в DeleteAllX(), даже если он может быть слегка упрощен.

Я подозреваю, что некоторая некоторая память не удалась (может быть очень тихой, если она была выполнена через malloc) и вызвать хаос в списке позже, когда вы удаляете, или что segfault происходит внутри деструктора ваши собственные объекты, когда вы вызываете delete. Поскольку многие ошибки программирования могут привести к этой ситуации, это выглядит более вероятным для меня, чем некоторая проблема из wxList, включая проблемы с распределением. Однако это достаточно легко проверить, просто отследите звонки вашим деструкторам, и вы скоро будете knwo, если оттуда придут segfault.

0

Я никогда не использовал WxWidgets, но я думаю, что DeleteAllX потерпит неудачу, если вы передаете такой параметр, который не находится в списке. Он не сработает в следующей строке:

node = node->GetNext(); 

Убедитесь, что этого не происходит в оригинальном приложении. Также вы можете поместить утверждает в указатель доступ, прежде чем получить что-то из указателя:

TestItem *item = node->GetData(); 
assert(item); 
if (item->x != x) { 
    node = node->GetNext(); 
    assert(node); 
    continue; 
} 

т.д.