2016-12-01 81 views
1

我知道如果必須在lambda中爲回調調用成員函數,我會在lambda中捕獲它,並且它工作正常。 但是我最近看到了一些崩潰,看起來成員函數在被這個指向的對象已經被銷燬之後被訪問。 簡而言之,在關閉時,對象被銷燬,但指針在lambda中傳遞,lambda被訪問並導致崩潰。在lambda中捕獲此選項的替代方法[C++]

所以,我試圖理解社區通常在這種情況下做什麼。我找不到太多,但我認爲shared_ptr可能是一個選項。

任何建議/線索將不勝感激,以幫助我理解和實施一個替代方案。

+1

你能發表一段代碼嗎? –

+0

你可以從'std :: enable_shared_from_this'繼承,使用'shared_from_this()'得到一個共享指針並捕獲一個'std :: shared_ptr'如果你想確保該對象仍然存在或者是一個'std: :weak_ptr'如果你想在做任何操作之前檢查它是否存在。 – skypjack

+0

我會說@TheEyesightDim。我們可以得到[mcve]嗎? – user4581301

回答

5

在C++中,您有責任追蹤對象的生命週期。

這意味着你必須跟蹤掌握指針和其他事物引用的事物的生存期,並確保它們不會像那些事一樣長壽。

您的任務失敗。你通過了捕獲指向周圍物體的指針的lambdas,就好像它們在哪裏糖果一樣,而不是直接插入對象的內部。

通過噴灑共享指針來解決生命期問題通常是一個壞主意。讓物體的壽命變得更模糊可能會減少立即崩潰的事件,但物體壽命的模糊不清使得程序無法工作。這個模糊的球或者擴展到包含你現在無法真正關閉的整個程序,或者它自己回到自身並自我延續,從而泄漏資源。

共享指針可用於狹義的情況下,在這種情況下,您擁有最佳建模爲共享所有權的定義的生命週期關係。這與「我的對象在指針前離開,所以我應該嘗試共享指針!」完全不一樣。你有一個對象生命期問題。你嘗試共享指針。現在你有兩個問題:原始對象生命期問題和共享指針問題。

回調是一個例子,您需要嚴格的生命週期規則。你回調多久了?你什麼時候停止?您如何緊急回收資源?你如何取消註冊回調?等

我已經寫回調系統,使用共享和弱指針。他們並不完美。這是我在google中找到的一個:broadcaster。聽衆存儲令牌時會說「繼續跟我說話」,當他們離開廣播公司時,他們就停止了。

+0

鎖定的weak_ptr仍然是最可靠和最簡單的方法,可以消除訂閱者在消息隊列中未實現的回調潛伏期間消失的情況。 –

+0

@RichardHodges請注意,'broadcaster '這樣做,'隊列'是廣播目標的向量。 – 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

可能他們接受了_sooner或later_ promise的_later_部分。 :-) – skypjack