std::remove не удаляется из std::vector

Вот моя проблема: в моем графическом интерфейсе есть несколько типов слушателей. Они хранятся в std::vector<WhateverListener*>

В моем графическом интерфейсе у меня есть метод removeListeners, и он выглядит так:

void Widget::removeListeners( Widget* widget )
{
    removeFocusListener((FocusListener*)widget);
    removeMouseListener((MouseListener*)widget);
    removeKeyboardListener((KeyboardListener*)widget);
    removeWidgetListener((WidgetListener*)widget);
}

По сути, я не думаю, что это должно иметь значение, как я его использую; они просто указатели. Я думаю, что std::remove просто сравнивает указатели, поэтому, если я предоставлю виджет*, он ни на что не повлияет (я думаю).

Функции удаления выглядят примерно так:

void Widget::removeWidgetListener( 
                                    WidgetListener *listener )
{
    widgetListeners.erase(
        std::remove(widgetListeners.begin(),
        widgetListeners.end(), listener),
        widgetListeners.end());
}

Итак, в деструкторе виджета я перебираю дочерние элементы виджета и вызываю removeListeners():

Widget::~Widget(void)
{

    for(std::vector<Widget*>::iterator it = getChildBegin();
        it != getChildEnd(); ++it)
    {
        (*it)->removeListeners(this);
        (*it)->parentWidget = NULL;
        (*it)->_container = NULL;
    }

}

Это не работает. После вызова удаления для виджета, который прослушивал своих дочерних элементов, у дочерних элементов все еще были слушатели.

Однако, если я вызываю методы remove напрямую, а виджет наследуется от слушателя, он работает:

Widget::~Widget(void)
{

    for(std::vector<Widget*>::iterator it = getChildBegin();
        it != getChildEnd(); ++it)
    {
        (*it)->removeWidgetListener(this);
        (*it)->parentWidget = NULL;
        (*it)->_container = NULL;
    }

}

Так почему же одно работает, а другое нет? Единственная разница, которую я замечаю, заключается в том, что в первом я привожу виджет к этому типу. Но я думал, что он просто сравнит указатели, и если они будут ==, он удалит их?


person jmasterx    schedule 24.04.2011    source источник
comment
Приведение типов сообщает компилятору, что я знаю тип этой переменной, даже если вы этого не знаете. Вы не должны указывать указатель на тип, которым он на самом деле не является. Является ли каждый Widget* переданным removeListeners() одновременно фокусом, мышью, клавиатурой и прослушивателем виджета?   -  person Asher Dunn    schedule 25.04.2011
comment
Лучше изменить заголовок, чтобы упомянуть erase, так как я и, возможно, другие сначала подумали, что это просто еще один вопрос, который не понимает, как работает remove. ;)   -  person Xeo    schedule 25.04.2011
comment
@Xeo: Да, я набрал этот ответ до того, как прочитал пост, а затем удалил его через 5 секунд :)   -  person sehe    schedule 25.04.2011


Ответы (2)


Корень вашей проблемы, кажется, является неправильным дизайном. Необходимость литья по мере того, как вы это делаете, подразумевает, что функция находится не в том месте. Из вашего сообщения неясно, каковы отношения между виджетами и различными типами классов Listener.

Вам нужно переосмыслить, откуда вы вызываете функцию removeListeners, и вместо того, чтобы помещать ее в деструктор базового класса, вы должны поместить ее в деструктор класса, который на самом деле знает, какой это тип прослушивателя (и вызывать только правильный) .

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

person hifier    schedule 25.04.2011

Боюсь, вас могут задеть идентификаторы объектов и виртуальные базовые классы в C++.

http://www.parashift.com/c++-faq-lite/multiple-inheritance.html

По сути, преобразование указателей в полиморфные базы не обязательно приведет к идентичным значениям указателя (например, при приведении к (void*)).

Он должен работать до тех пор, пока вы сохраняете тот же самый тип указателя, который вы привели во время удаления, но я не уверен не глядя на иерархию классов вашего кода/виджета.

person sehe    schedule 24.04.2011
comment
Поэтому я должен динамически приводить перед удалением, а затем - person jmasterx; 25.04.2011
comment
Да, но только к тому типу, который вы их вставляете. Я лично думаю, что приведения - это запах, но вам обязательно следует рассмотреть dynamic_cast‹› для такого типа сценария. - person sehe; 25.04.2011