Спасибо, ОП, за публикацию более конкретного примера. Это заставило меня сделать необычный шаг - опубликовать второй ответ. Я надеюсь, что вы найдете это полезным.
Что я собираюсь сделать здесь, это показать вам альтернативную иерархию классов, которая, я надеюсь, вы увидите, лучше подходит для задачи, которую вы хотите решить. Я просто собираюсь сосредоточиться на отношениях между классом Button
(в этом заключается фундаментальная проблема) и тем, что я собираюсь назвать ButtonHandler
.
Теперь ButtonHandler
может быть чем угодно - Button
определенно не волнует, что это такое - его просто нужно уведомить, когда кнопка, которой он владеет , нажата или отпущена. Как только вы поймете, что это лежит в основе проблемы, решение становится более очевидным. Конечно, вы можете применить этот подход к своему Sender
классу (просто получить его из ButtonHandler
и реализовать OnPress
и OnRelease
), но я оставлю этот бит вам.
Наконец, в качестве небольшого обледенения на пироге мои Button
добавили и удалили себя в / из того списка, о котором вы говорили, чтобы их можно было легко опрашивать в каком-то цикле.
Хорошо, поехали. Давайте начнем с нашего ButtonHandler
(поскольку это должно быть объявлено перед классом Button
), что достаточно просто:
#include <set>
#include <iostream>
class Button;
// Our abstract button handler - derive concrete handlers from this
class ButtonHandler
{
public:
// Constructor
ButtonHandler ();
// Destructor (must be virtual!)
virtual ~ButtonHandler ();
virtual void OnPress () = 0;
virtual void OnRelease () = 0;
private:
Button *m_button;
};
ОК, там особо не на что смотреть. Обратите внимание на чистые виртуальные методы OnPress
и OnRelease
, которые существуют для конкретных классов, производных от этого для переопределения, и предварительное объявление класса Button
. Нам нужно будет реализовать тела конструктора и деструктора позже, после того как они увидят полное объявление класса Button
, поскольку им нужно вызывать методы для него.
Теперь для самого класса Button
и связанного с ним ButtonList
. Обратите внимание, что, опять же, поскольку он имеет дело только с указателями на Button
s, ButtonList
не нужно видеть полное объявление класса Button
, что здесь очень удобно:
std::set<Button *> ButtonList;
// class Button
class Button
{
public:
// Constructor
Button (ButtonHandler *button_handler)
{
m_button_handler = button_handler;
ButtonList.insert (this);
}
// Destructor
~Button () { ButtonList.erase (this); }
// Poll the button state
void Poll ()
{
// This would normally check (in hardware) whether button is pressed, but here we just fake it
m_button_handler->OnPress ();
m_button_handler->OnRelease ();
}
private:
ButtonHandler *m_button_handler;
};
// Poll all the buttons that exist
void PollAllButtons ()
{
for (auto button : ButtonList) { button->Poll (); }
}
Так что здесь происходит? Ну:
- Конструктор
Button
передает указатель на ButtonHander
, чтобы он мог при необходимости вызывать его методы OnPress
и OnRelease
.
- Волшебство STL (когда доступно!) Делает реализацию
ButtonList
тривиальной. Он просто использует
std::set
, что задокументировано здесь (см. Примеры на этой странице и связанных страницах). Кнопки добавляются и удаляются из этого списка по мере их появления.
PollAllButtons
перебирает все Button *
, хранящиеся в ButtonList
, и вызывает то, что я выбрал, для каждого из них Poll
, что, в свою очередь, вызывает связанные ButtonHandler
s OnPress
и OnRelease
методы по мере необходимости. При этом используется нечто, называемое «циклом в диапазоне», которое, AFAIK, работает со всеми классами контейнеров STL.
Теперь реализация конструктора и деструктора ButtonHandler
. Они просто создают и удаляют связанную кнопку (так что Buttonhandler
владеет кнопкой):
// ButtonHandler constructor - implementation
ButtonHandler::ButtonHandler ()
{
m_button = new Button (this);
}
// Buttonhandler destructor - implementation
ButtonHandler::~ButtonHandler () { delete m_button; }
И, наконец, здесь есть конкретный обработчик кнопок и минимальная тестовая программа, чтобы показать вам, что все эти вещи действительно работают. Вы заметите (и это довольно приятно), что это не отличается от предыдущего кода, который я опубликовал (который был слишком умным ):
// An example concrete button handler
class MyButtonHandler : public ButtonHandler
{
public:
// Constructor
MyButtonHandler (int handler_id) { m_handler_id = handler_id; }
void OnPress () override { std::cout << "MyButtonHandler::OnPress (" << m_handler_id << ")" << std::endl; }
void OnRelease () override { std::cout << "MyButtonHandler::OnRelease (" << m_handler_id << ")" << std::endl; }
// ...
private:
int m_handler_id;
};
// Test program
int main ()
{
MyButtonHandler bh1 (1);
MyButtonHandler bh2 (2);
PollAllButtons ();
}
Выход:
MyButtonHandler::OnPress (1)
MyButtonHandler::OnRelease (1)
MyButtonHandler::OnPress (2)
MyButtonHandler::OnRelease (2)
Запустите его на Wandbox .
Итак, поехали. Очень отличается от вашего (потому что вы отправились не на тот путь, и пути назад не было). Я предлагаю вам взять его и бежать с ним, удачи.