В моем проекте есть система событий. Вы можете подключить обратный вызов к событию, и каждый раз, когда событие отправляется, вызываются ваши обратные вызовы.
При подключении к событию вы получаете токен. Пока токен не уничтожен, соединение активно:
class A
{
A()
{
event_connection = get_dispatcher().connect(event, std::bind(member_function, this));
}
void member_function()
{
dummy_instance++; //any action that uses this field
}
// changed from shared to unique to avoid confusion
//std::shared_ptr<event_connection_token> event_connection;
std::unique_ptr<event_connection_token> event_connection;
dummy_type dummy_instance;
}
Однако проблема возникает в следующем сценарии:
A
начинаетсяdummy_instance
уничтоженevent_connection
еще не был разрушенПоэтому мне нужен мой event_connection_token
всегда быть уничтоженным перед любым членом класса, который использует обратный вызов. Теперь, если я хочу, чтобы 100 других программистов использовали эту систему обратного вызова событий, было бы непрофессионально ожидать, что они всегда освобождают event_connection_token
первый во всех классах, которые они когда-либо делают. Наконец мы подошли к вопросу:
Как я могу обеспечить, чтобы каждый клиент удалял event_connection_token
прежде чем что-либо еще в клиентском классе будет уничтожено?
Я ищу либо:
РЕДАКТИРОВАТЬ: Вопрос, помеченный как дубликат, не решает мою проблему. Я знаю порядок уничтожения объектов или даже явно вызывающий .reset()
в деструкторе исправит мою проблему. Это, однако, не решение моей проблемы. Проблема в том, что я не хочу полагаться на то, что каждый разработчик в проекте помнит об этом правиле (так как эта система обратного вызова событий должна использоваться во многих местах кода).
Просто поменяйте местами декларации
class A
{
A()
{
event_connection = get_dispatcher().connect(event, std::bind(member_function, this));
}
void member_function()
{
dummy_instance++; //any action that uses this field
}
// changed from shared to unique to avoid confusion
//std::shared_ptr<event_connection_token> event_connection;
dummy_type dummy_instance;
std::unique_ptr<event_connection_token> event_connection;
}
порядок уничтожения в обратном порядке декларации (потому что конструирование происходит в порядке декларации). При уничтожении экземпляра сначала вызывается деструктор экземпляра, затем event_connection
уничтожен последним dummy_instance
уничтожается (строительство происходит в обратном порядке).
Я думаю, вам придется смириться с тем фактом, что есть некоторые правила, которым нужно следовать, чтобы гарантировать это, если вы не хотите слишком далеко мешать им делать «глупые» вещи (а я не даже думаю, что вы можете покрыть все угловые дела в любом случае).
Если вы не можете требовать их поставить event_connection
наконец, затем вы должны запретить им добавлять его с помощью композиции (и даже тогда, если вы добавите его вообще, вы в конечном итоге потребуете от них явного удаления указателя). Это исключило бы наличие event_connection
в A
в первую очередь, а точнее только разрешить event_connection
иметь ссылку на A
, который будет хорошо работать, если вы используете умный указатель (за исключением того, что это будет означать, что A
объект будет оставаться до тех пор, пока event_connection
остается).
Вы можете попытаться исключить фактическую реализацию обратного вызова в отдельный класс, а затем объединить его в класс «хранитель»:
class ACallback
{
public:
void member_function()
{
dummy_instance++; //any action that uses this field
}
private:
dummy_type dummy_instance;
}
class A
{
A(ACallback *callback) : callback(callback)
{
event_connection = get_dispatcher().connect(event, std::bind(ACallback::member_function, callback));
}
~A()
{
// make sure callback will not be used any more
}
std::unique_ptr<ACallback> callback;
std::unique_ptr<event_connection_token> event_connection;
}