Как обеспечить, чтобы данный ресурс всегда был удален первым?

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

При подключении к событию вы получаете токен. Пока токен не уничтожен, соединение активно:

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;
}

Однако проблема возникает в следующем сценарии:

  1. Деконструкция класса A начинается
  2. поле dummy_instance уничтожен
  3. Сейчас происходит событие
  4. Обратный вызов называется, потому что event_connection еще не был разрушен
  5. Обратный вызов пытается получить доступ к освобожденной памяти, и программа вылетает

Поэтому мне нужен мой event_connection_token всегда быть уничтоженным перед любым членом класса, который использует обратный вызов. Теперь, если я хочу, чтобы 100 других программистов использовали эту систему обратного вызова событий, было бы непрофессионально ожидать, что они всегда освобождают event_connection_token первый во всех классах, которые они когда-либо делают. Наконец мы подошли к вопросу:

Как я могу обеспечить, чтобы каждый клиент удалял event_connection_token прежде чем что-либо еще в клиентском классе будет уничтожено?

Я ищу либо:

  • умный дизайн, который гарантирует, что токен всегда будет удален первым, даже если программисты даже не думают об этом, или
  • проверка во время компиляции / выполнения, которая позволит программистам знать, что им нужно изменить код, чтобы токен был удален первым.

РЕДАКТИРОВАТЬ: Вопрос, помеченный как дубликат, не решает мою проблему. Я знаю порядок уничтожения объектов или даже явно вызывающий .reset() в деструкторе исправит мою проблему. Это, однако, не решение моей проблемы. Проблема в том, что я не хочу полагаться на то, что каждый разработчик в проекте помнит об этом правиле (так как эта система обратного вызова событий должна использоваться во многих местах кода).

2

Решение

Просто поменяйте местами декларации

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 остается).

1

Другие решения

Вы можете попытаться исключить фактическую реализацию обратного вызова в отдельный класс, а затем объединить его в класс «хранитель»:

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;
}
0

По вопросам рекламы [email protected]