2016-12-01 2 views
1

Я знаю, что если мне нужно вызвать функции-члены в лямбда для обратных вызовов, я фиксирую это в лямбда, и он работает хорошо. Но я недавно видел некоторый сбой, и кажется, что функция-член открывается после того, как объект, на который это указывает, уже уничтожен. В двух словах, при выключении объект уничтожается, но указатель передается в лямбда, к которому осуществляется доступ, и приводит к сбою.Альтернатива для этого в лямбда [C++]

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

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

+1

Можете ли вы опубликовать фрагмент кода? –

+0

Вы можете наследовать от 'std :: enable_shared_from_this', использовать' shared_from_this() ', чтобы получить общий указатель и либо захватить' std :: shared_ptr'if, что вы хотите быть уверены, что объект все еще жив, или 'std: : weak_ptr', если вы хотите проверить, жив ли он или нет, чтобы выполнить какую-либо операцию. – skypjack

+0

Я сделаю то, что @TheEyesightDim.Можем ли мы получить [mcve]? – user4581301

ответ

5

В C++ вы несете ответственность за отслеживание жизни объектов.

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

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

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

Общие указатели могут использоваться в узких ситуациях, когда у вас есть определенные отношения времени жизни, которые лучше всего моделируются как совместное владение. Это совсем не то же самое, что «у меня есть объекты, уходящие перед их указателями, поэтому я должен попробовать общий указатель!» У вас есть проблема с жизненным циклом объекта. Вы пытаетесь использовать общий указатель. Теперь у вас есть две проблемы: первоначальная проблема с жизненным сроком объекта и проблема с общим указателем.

Обратные вызовы - пример случая, когда вам нужны строгие правила жизни. Как долго вы звоните? Когда вы остановитесь? Как срочно вы перерабатываете ресурсы? Как вы отменили регистрацию обратного вызова? И т. Д.

Я написал системы обратного вызова, которые используют общие и слабые указатели. Они не идеальны. Вот один, который я нашел в google: broadcaster. Слушатель хранит токены, чтобы сказать «продолжайте говорить со мной», когда они уходят, вещатель останавливается на нем.

+0

заблокированный weak_ptr по-прежнему является самым надежным и простым способом устранения случая пересечения, когда абонент уходит, а невыполненный обратный вызов скрывается в очереди сообщений. –

+0

@RichardHodges Обратите внимание, что 'broadcastcaster ' делает то, где «очередь» - это вектор целей, на которые ведется передача. – Yakk

0

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

#include <memory> 
#include <chrono> 
#include <thread> 
#include <mutex> 

using namespace std::literals; 

// some external service that will send us events in a callback. 
// normally of course we'd have some means to turn these off too. 
// However for this demo we'll ignore that for now 
void subscribe_for_events(std::function<void()> f); 

struct might_go_away : std::enable_shared_from_this<might_go_away> 
{ 
    static std::shared_ptr<might_go_away> create() { 
     auto p = std::make_shared<might_go_away>(); 
     p->start(); 
    } 

    might_go_away() {} 

private: 

    using mutex_type = std::mutex; 
    using lock_type = std::unique_lock<mutex_type>; 

    // handy helper to deliver a weak pointer to ourselves 
    auto weak_self() { return std::weak_ptr<might_go_away>(shared_from_this()); } 

    // do startup things here, like subscribing to other services etc 
    void start() { 
     subscribe_for_events([this, weak = this->weak_self()] 
     { 
      // don't touch 'this' until we have successfully locked the weak ptr 
      if (auto self = weak.lock()) { 
       // we know we're alive. the variable 'self' will hold the strong count > 0 
       // this is a good place to take locks 
       handle_event(lock_type(mutex_)); 
      } 
     }); 
    } 

    void handle_event(lock_type) { 
     // do things when notified by some event source. 
     // we are safe here. `this` will not go away and we own the mutex 
     // we will release the lock when this function exits through RAII. 
     // PLUS, because the lock was moved in, we own it. We can release it early if we wish. 
    } 

    mutex_type mutex_; 
}; 

Конечно, в производственном коде shared_ptr будет обернут в потребительской стороне класса ручки.

Мы предпочитаем семантику по возможности, где это возможно.

struct active_thing 
{ 
    using implementation_class = might_go_away; 
    using implementation_type = std::shared_ptr<implementation_class>; 

    active_thing() : impl_(implementation_class::create()) {} 

    // methods here 

private: 
    implementation_type impl_; 
}; 


int main() 
{ 
    { 
    auto a = active_thing(); 
    std::this_thread::sleep_for(5s); 
    } 
} 
+0

'weak_self' ... В ожидании предстоящего' weak_from_this' ... :-) – skypjack

+0

@skypjack взял их только 6 лет ... –

+1

Возможно, они взяли _later_ часть обещания _сусера или позже. :-) – skypjack

Смежные вопросы