Как я могу гарантировать, что срок службы объекта соответствует продолжительности функции-члена? - PullRequest
0 голосов
/ 03 апреля 2012

Я использовал обратные вызовы, чтобы уменьшить связь между некоторыми классами C ++. Чтобы определить термины: я буду называть класс, делающий обратные вызовы вызывающим, а класс, получающий обратный вызов, вызываемым. Обычно (но не обязательно) вызываемый абонент владеет абонентом. По замыслу звонящий не знает о вызываемом абоненте.

Я сталкиваюсь с проблемой, связанной с продолжительностью жизни объекта вызывающего: у него нет гарантии, что он останется живым после любого произвольного обратного вызова. Возьмите этот простой пример:

void caller::f()
{
    /* Some work */
    if (...)
    {
        /* [1] Execute callback */
        _callee->callback(this);
    }
    /* [2] Some more work */
}

Скажите, что вызываемый абонент динамически назначил вызывающего и зарегистрировался для обратного вызова специально для ожидания возникновения определенного условия. Когда это произойдет, вызываемый будет удалять вызывающего изнутри обратного вызова в [1]. Если это так, управление вернется к caller :: f, но this будет удалено, и любой код в [2] более чем вероятен сбой.

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

Я полагаю, что возможное решение вращается вокруг boost::shared_ptrs и enable_shared_from_this, хотя я никогда не использовал его. Поскольку эти обратные вызовы выполняются очень часто (более 40 раз в секунду) на мобильных устройствах с ограниченной вычислительной мощностью, я также беспокоюсь о накладных расходах, связанных с созданием и передачей такого количества shared_ptrs.

Делегирование - довольно распространенная модель в Objective-C. Я гораздо менее знаком с общими шаблонами проектирования C ++. Есть ли быстрое и простое решение этой проблемы? Если нет, то как этот дизайн обычно выполняется в C ++?

Ответы [ 3 ]

1 голос
/ 03 апреля 2012

Продолжайте и используйте общий указатель, хотя, если возможно, используйте std::shared_ptr вместо boost::shared_ptr. Он находится в (текущей) стандартной библиотеке, поэтому нет необходимости добавлять ненужную зависимость надстройки. Если вы уже используете boost, то это тоже нормально.

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

Если ваше устройство действительно может выполнять обратный вызов более 40 раз в секунду, я сомневаюсь, что оно будет иметь какие-либо проблемы с общими указателями. Не преждевременно оптимизируйте скорость выполнения. СДЕЛАЙТЕ преждевременную оптимизацию для безопасности и здравомыслия.

1 голос
/ 03 апреля 2012

Обычный способ обойти код, который вы больше не можете выполнять, - вызвать исключение. Это должно быть сделано вызываемым пользователем в том месте, где он обычно возвращается к вызывающему, после того как удалит вызывающего. Исключение будет зафиксировано в коде вызывающей стороны в конце функции.

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

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

1 голос
/ 03 апреля 2012

Когда вызывающий абонент delete s, вызывается деструктор вызывающего абонента. Именно там вы должны убедиться, что f закончено.

Я предполагаю, f - это нить, поэтому самое простое решение будет:

нить:

running = true;
while (!must_exit)
    /* do something */

destuctor:

thread->must_exit = true;
while (thread->running)
    sleep(a_little);
/* continue with destruction */

Если f не является потоком, тот же принцип может применяться, когда f сообщает своему объекту (и через него деструктору), когда он работает, а когда нет.


Если вы не хотите использовать деструкторный подход, вы все равно можете реализовать эту функцию с помощью функции, которую вызывает вызываемый объект, сообщая f никогда больше не запускаться и ждать, пока он не остановится. Затем вызываемый абонент продолжает удаление вызывающего абонента.

Так что-то вроде этого:

void caller::f()
{
    if (being_deleted)
        return;
    running = true;
    /* Some work */
    if (...)
    {
        /* [1] Execute callback */
        _callee->callback(this);
    }
    /* [2] Some more work */
    running = false;
}

void caller::make_f_stop()
{
    being_deleted = true;
    while (running)
        sleep(a_little);
}
...